L3-图论-第08课 图的遍历

图的遍历是指,从给定图中任意指定的顶点(称为初始点)出发,按照某种搜索方法沿着图的边访问图中的所有顶点,使每个顶点仅被访问一次,这个过程称为图的遍历。遍历过程中得到的顶点序列称为图遍历序列。

图的遍历过程中,根据搜索方法的不同,又可以划分为两种搜索策略:

  • 深度优先搜索(DFS,Depth First Search)

  • 广度优先搜索(BFS,Breadth First Search)

深度优先搜索 DFS

DFS 全称是(Depth First Search),中文名是深度优先搜索,是一种用于遍历或搜索树或图的算法。所谓深度优先,就是说每次都尝试向更深的节点走。

该算法讲解时常常与 BFS 并列,但两者除了都能遍历图的连通块以外,用途完全不同,很少有能混用两种算法的情况。

DFS 最显著的特征在于其 「递归调用自身」 。同时与 BFS 类似,DFS 会对其访问过的点打上访问标记,在遍历图时跳过已打过标记的点,以确保 「每个点仅访问一次」 。符合以上两条规则的函数,便是广义上的 DFS。

算法思想

  1. 从顶点 vi 出发, 访问, 标记, 找邻接顶点 vi1
  2. 从vi1 出发, DFS访问和它邻接的所有顶点
  3. 转到第一步, 直到所有和vi邻接的顶点全部被访问.
  4. 继续选取图中其他未访问的顶点作为起始顶点, 转到第一步.

具体地说,DFS 大致结构如下:

    DFS(v) // v 可以是图中的一个顶点,也可以是抽象的概念,如 dp 状态等。      在 v 上打访问标记      for u in v 的相邻结点        if u 没有打过访问标记 then          DFS(u)        end      end    end

该算法通常的时间复杂度为 ,空间复杂度为 ,其中 表示点数, 表示边数。注意空间复杂度包含了栈空间,栈空间的空间复杂度是 的。在平均 遍历一条边的条件下才能达到此时间复杂度,例如用前向星或邻接表存储图;如果用邻接矩阵则不一定能达到此复杂度。

参考代码

以链式前向星为例:

void dfs(int u) {  cout <" ";  vis[u] = 1;  for (int ei = head[u]; ei; ei = e[ei].next) {    if (!vis[e[i].to]) {      dfs(e[i].to);    }  }}

DFS 序列

DFS 序列是指 DFS 调用过程中访问的节点编号的序列。

我们发现,每个子树都对应 DFS 序列中的连续一段(一段区间)。

一般图上 DFS

对于非连通图,只能访问到起点所在的连通分量。

对于连通图,DFS 序列通常不唯一。

注:树的 DFS 序列也是不唯一的。

在 DFS 过程中,通过记录每个节点从哪个点访问而来,可以建立一个树结构,称为 DFS 树。DFS 树是原图的一个生成树。

[USACO06DEC]Cow Picnic S

题目描述

The cows are having a picnic! Each of Farmer John's K (1 ≤ K ≤ 100) cows is grazing in one of N (1 ≤ N ≤ 1,000) pastures, conveniently numbered 1...N. The pastures are connected by M (1 ≤ M ≤ 10,000) one-way paths (no path connects a pasture to itself).

The cows want to gather in the same pasture for their picnic, but (because of the one-way paths) some cows may only be able to get to some pastures. Help the cows out by figuring out how many pastures are reachable by all cows, and hence are possible picnic locations.

K(1≤K≤100)只奶牛分散在N(1≤N≤1000)个牧场.现在她们要集中起来进餐.牧场之间有M(1≤M≤10000)条有向路连接,而且不存在起点和终点相同的有向路.她们进餐的地点必须是所有奶牛都可到达的地方.那么,有多少这样的牧场呢?

输入格式

Line 1: Three space-separated integers, respectively: K, N, and M

Lines 2..K+1: Line i+1 contains a single integer (1..N) which is the number of the pasture in which cow i is grazing.

Lines K+2..M+K+1: Each line contains two space-separated integers, respectively A and B (both 1..N and A != B), representing a one-way path from pasture A to pasture B.

输出格式

Line 1: The single integer that is the number of pastures that are reachable by all cows via the one-way paths.

输入输出样例

  • 输入 #1复制
2 4 4231 21 42 33 4
  • 输出 #1复制

2

说明/提示

The cows can meet in pastures 3 or 4.

分析

牧场个数是 1000 个, 使用邻接矩阵存储空间上是没问题的. 求所有奶牛都能到的牧场, 那就让奶牛遍历所有的能到达的点, 并且标记好. 那么被所有奶牛标记过的牧场就是所求的.

邻接矩阵

#include #include using namespace std;

const int MAXN = 1000 + 6;

int k, n, m, ans;int g[MAXN][MAXN], cow[MAXN];int vis[MAXN], mk[MAXN];

int dfs(int x){ mk[x]++; vis[x] = 1; for (int i = 1; i <= n; i++) {  if (g[x][i] && vis[i] == 0)  {   dfs(i);  } }}

int main(){ ios::sync_with_stdio(false); cin >> k >> n >> m;

 for (int i = 1; i <= k; i++) {  cin >> cow[i]; }

 for (int i = 1; i <= m; i++) {  int x, y;  cin >> x >> y;  g[x][y] = 1; }

 for (int i = 1; i <= k; i++) {  memset(vis, 0, sizeof(vis));  dfs(cow[i]); }

 for (int i = 1; i <= n; i++) {  if (mk[i] == k)   ans++; }

 cout < return 0;}

广度优先搜索 BFS

BFS 全称是 [Breadth First Search] 中文名是宽度优先搜索,也叫广度优先搜索。是图上最基础、最重要的搜索算法之一。

所谓宽度优先。就是每次都尝试访问同一层的节点。如果同一层都访问完了,再访问下一层。

这样做的结果是,BFS 算法找到的路径是从起点开始的 「最短」 合法路径。换言之,这条路所包含的边数最小。

在 BFS 结束时,每个节点都是通过从起点到该点的最短路径访问的。

算法思想

伪代码:

bfs(s) {  q = new queue()  q.push(s);  visited[s] = true  while (!q.empty()) {    u = q.pop()    for each edge(u, v) {      if (!visited[v]) {        q.push(v)        visited[v] = true      }    }  }}

参考代码

C++:

void bfs(int u) {  while (!Q.empty())    Q.pop();  Q.push(u);  vis[u] = 1;  d[u] = 0;  p[u] = -1;  while (!Q.empty()) {    u = Q.front();    Q.pop();    for (int i = head[u]; i; i = e[i].next) {      if (!vis[e[i].to]) {        Q.push(e[i].to);        vis[e[i].to] = 1;        d[e[i].to] = d[u] + 1;        p[e[i].to] = u;      }    }  }}void restore(int x) {  vector<int> res;  for (int v = x; v != -1; v = p[v]) {    res.push_back(v);  }  std::reverse(res.begin(), res.end());  for (int i = 0; i printf("%d", res[i]);  puts("");}

具体来说,我们用一个队列 Q 来记录要处理的节点,然后开一个 布尔数组来标记某个节点是否已经访问过了。

开始的时候,我们把起点 s 以外的节点的 vis 值设为 0,意思是没有访问过。然后把起点 s 放入队列 Q 中。

之后,我们每次从队列 Q 中取出队首的点 u,把 u 相邻的所有点 v 标记为已经访问过了并放入队列 Q。

直到某一时刻,队列 Q 为空,这时 BFS 结束。

在 BFS 的过程中,也可以记录一些额外的信息。比如上面的代码中,d 数组是用来记录某个点到起点的距离(要经过的最少边数),p 数组是记录从起点到这个点的最短路上的上一个点。

有了 d 数组,可以方便地得到起点到一个点的距离。

有了 p 数组,可以方便地还原出起点到一个点的最短路径。上面的 restore 函数就是在做这件事:restore(x) 输出的是从起点到 x 这个点所经过的点。

时间复杂度

空间复杂度 (vis 数组和队列)

BFS 序列

BFS 序列通常也不唯一。

类似的我们也可以定义 BFS 树:在 BFS 过程中,通过记录每个节点从哪个点访问而来,可以建立一个树结构,即为 BFS 树。

应用

  • 在一个无权图上求从起点到其他所有点的最短路径。

  • 在 时间内求出所有连通块。(我们只需要从每个没有被访问过的节点开始做 BFS,显然每次 BFS 会走完一个连通块)

  • 如果把一个游戏的动作看做是状态图上的一条边(一个转移),那么 BFS 可以用来找到在游戏中从一个状态到达另一个状态所需要的最小步骤。

  • 在一个边权为 0/1 的图上求最短路。(需要修改入队的过程,如果某条边权值为 0,且可以减小边的终点到图的起点的距离,那么把边的起点加到队列首而不是队列尾)

  • 在一个有向无权图中找最小环。(从每个点开始 BFS,在我们即将抵达一个之前访问过的点开始的时候,就知道遇到了一个环。图的最小环是每次 BFS 得到的最小环的平均值。)

  • 找到一定在 最短路上的边。(分别从 a 和 b 进行 BFS,得到两个 d 数组。之后对每一条边 ,如果 ,则说明该边在最短路上)

  • 找到一定在 最短路上的点。(分别从 a 和 b 进行 BFS,得到两个 d 数组。之后对每一个点 v,如果 ,则说明该点在最短路上)

  • 找到一条长度为偶数的最短路。(我们需要一个构造一个新图,把每个点拆成两个新点,原图的边 变成 和 。对新图做 BFS, 和 之间的最短路即为所求)

P5318 【深基18.例3】查找文献

题目描述

小K 喜欢翻看洛谷博客获取知识。每篇文章可能会有若干个(也有可能没有)参考文献的链接指向别的博客文章。小K 求知欲旺盛,如果他看了某篇文章,那么他一定会去看这篇文章的参考文献(如果他之前已经看过这篇参考文献的话就不用再看它了)。

假设洛谷博客里面一共有 篇文章(编号为 1 到 n)以及 条参考文献引用关系。目前小 K 已经打开了编号为 1 的一篇文章,请帮助小 K 设计一种方法,使小 K 可以不重复、不遗漏的看完所有他能看到的文章。

这边是已经整理好的参考文献关系图,其中,文献 X → Y 表示文章 X 有参考文献 Y。不保证编号为 1 的文章没有被其他文章引用。


请对这个图分别进行 DFS 和 BFS,并输出遍历结果。如果有很多篇文章可以参阅,请先看编号较小的那篇(因此你可能需要先排序)。

输入格式

输出格式

输入输出样例

  • 输入 #1复制
8 91 21 31 42 52 63 74 74 87 8
  • 输出 #1复制
1 2 5 6 3 7 8 41 2 3 4 5 6 7 8

分析

文章数是 所以邻接矩阵不能使用, 得用邻接表或者链式前向星来存储才可以. 要求输出时从编号小的开始, 所以需要对边进行排序.

参考代码

#include #include #include #include #include using namespace std;

const int MAXN = 1e6 + 8;struct Edge{ int u, v;} es[MAXN];vector hd[MAXN];bool vis[MAXN] = {0};int n, m;bool cmp(Edge &e1, Edge &e2){if (e1.v == e2.v)return e1.u return e1.v }void dfs(int x){ vis[x] = 1; cout <" ";for (int i = 0; i  {  int point = es[hd[x][i]].v;if (!vis[point])  {   dfs(point);  } }}void bfs(int x){ queue q; memset(vis, 0, sizeof(vis)); q.push(x); cout <" "; vis[x] = true;while (!q.empty()) {  int u = q.front();for (int i = 0; i   {   int point = es[hd[u][i]].v;if (!vis[point])   {    q.push(point);    cout <" ";    vis[point] = true;   }  }  q.pop(); }}int main(){ ios::sync_with_stdio(false); cin >> n >> m;for (int i = 1; i <= m; i++) {  cin >> es[i].u >> es[i].v; } sort(es + 1, es + 1 + m, cmp);for (int i = 1; i <= m; i++)  hd[es[i].u].push_back(i); dfs(1); cout < bfs(1);return 0;}

题单

  • P5318 【深基18.例3】查找文献
  • P3916 图的遍历
  • P1113 杂务
  • P1807 最长路
  • P1127 词链
  • P2853 [USACO06DEC]Cow Picnic S

云帆优培订阅号:

云帆优培服务号:

云帆优培老师联系方式:

云帆老师

微信:

云帆优培介绍

c++ 遍历所有点且距离最短_L3图论第08课 图的遍历相关推荐

  1. c++ 遍历所有点且距离最短_图解:最短路径之迪杰斯特拉算法

    小禹禹们,你们好,景禹最近已经开学,忙着准备毕业答辩的事情,这才抽身个大家更新文章,还请莫怪.生活实属不易,有时候让人有点儿焦头烂额,甚至想让景禹放弃继续更新文章,可是千百号人默默地关注者景禹,当然也 ...

  2. c++ 遍历所有点且距离最短_C/C++ 图的最短路径 Dijkstra 算法

    作者:小石王 链接:https://www.cnblogs.com/xiaoshiwang/p/9442391.html 图的最短路径的概念: 一位旅客要从城市A到城市B,他希望选择一条途中中转次数最 ...

  3. c++ 遍历所有点且距离最短_编程小白暑期进阶笔记41-C语言数据结构与算法图遍历的应用...

    基于广度优先遍历算法的应用 思考题: (思考题答案: BFS(广度优先遍历)在一般的带权图中是不能解决最短路问题,了解BFS的都知道,BFS是根据节点到源节点之间的节点数遍历的,也就是先访问离源节点节 ...

  4. 第七十四课 图的遍历(BFS)

    广度优先相当于对顶点进行分层,层次遍历. 在Graph.h中添加BFS函数: 1 #ifndef GRAPH_H 2 #define GRAPH_H 3 4 #include "Object ...

  5. Boost:双图bimap遍历的测试程序

    Boost:双图bimap遍历的测试程序 实现功能 C++实现代码 实现功能 双图bimap遍历的测试程序 C++实现代码 #define _CRT_SECURE_NO_DEPRECATE #defi ...

  6. 图的遍历(BFS、DFS)

    前文使用邻接矩阵法和邻接链表法实现了图结构(MatrixGraph .ListGraph ),本节使用两种算法进行图的遍历 目录 1.定义与概括 2.广度优先搜索算法 3.深度优先搜索算法 4.小结 ...

  7. 平原上,一群蜜蜂离开蜂巢采蜜,要连续采集5片花丛后归巢,已知5片花丛相对蜂巢的坐标,请你帮它们规划一下到访花儿的顺序,以使飞行总距离最短。

    蜂巢在坐标(0,0)的位置,有五处花丛,蜜蜂从蜂巢出发,要把五处花丛的花蜜采完再回到蜂巢,最短距离是多少.输入说明:一行输入,10个数分别是五处花丛的坐标(x1,y1,x2,y2,x3,y3,x4,y ...

  8. 距离最短原则的离散点连接 Python实现

    如何用Python实现这张图,这张图就是随机生成几个点,然后将这些点按照距离最短原则连成一条线

  9. 如何求地球上两点之间的最短距离_例谈平行线上两动点之间距离最短问题

    初中几何中有一类关于距离最短的问题,这些问题最终都会转化为"垂线段最短"或"两点之间线段最短".本文就一类平行线上两动点之间距离最短问题,谈谈笔者对此的分析和见 ...

最新文章

  1. java notifier_Java学习笔记---4.Java的分支循环语句
  2. 什么叫做多媒体计算机技术,多媒体计算机系统是什么
  3. 机器学习如何改变大数据管理
  4. python学习内容大全_python学习内容大全
  5. LeetCode 面试题55 二叉树的深度
  6. java学习(142):file类的基本创建
  7. 红橙Darren视频笔记 RecyclerView基本使用
  8. JSPatch真强大!
  9. 金山云笔试题:AKM函数
  10. cssrem转换工具_微信小程序开发-rem转换rpx小工具
  11. 什么是架构?架构师的职责是什么?
  12. gtx1050ti最稳定的驱动_英伟达gtx1050ti现在用什么版本驱动比较好?
  13. 海贼王 动漫 全集目录 分章节 精彩打斗剧集
  14. bilibili直播 斗鱼直播等直播工具黑屏怎么办?
  15. libuv介绍与编译
  16. 视频添加图片背景怎么操作
  17. 京东数科开源区块链底层引擎JD Chain,区块链已成其第四大核心技术
  18. PyAutoFEP Tutorial--基于Gromacs
  19. python 里面的无穷大与无穷小
  20. 如果mysql磁盘满了,会发生什么?还真被我遇到了!

热门文章

  1. 88. Leetcode 剑指 Offer 14- I. 剪绳子 (动态规划-基础题)
  2. 文巾解题 397. 整数替换
  3. Python应用实战-sql操作groupby常用技巧
  4. 万字长文,一文读懂Linux的常规操作(墙裂建议收藏)
  5. 深度学习核心技术精讲100篇(四十八)-TB级的日志监控系统很难?带你使用ELK轻松搭建日志监控系统
  6. 模型独立学习:多任务学习与迁移学习
  7. 用Intersects方式联接地理数据,如何进行地理数据分析
  8. TensorFlow官方入门实操课程-一个神经元的网络(线性曲线预测)
  9. android 4.3 操作源码实现系统截屏(暂无移植性)
  10. JVM SandBox 的技术原理与应用分析