【数据结构与算法基础】代码仓库:https://github.com/jinrunheng/datastructure-and-algorithm

一:数据结构遍历的意义

图是一种数据结构,每种数据结构,都必须有“遍历”的方式,很多算法,本质都是遍历。

对于线性数据结构

image.png

遍历是从头到尾访问线性结构的每个元素。

对于一棵二叉树,也存在着多种遍历方式,按照访问树的节点的顺序可以分为:前序,中序,后序遍历以及层序遍历。前序,中序,后序遍历都是一种深度优先遍历,按层遍历是一种广度优先遍历。

image.png

图的遍历整体也可以分为两大类:深度优先遍历与广度优先遍历。

那么图的遍历和树的遍历有什么本质的不同呢?

image.png

假设我的图结构如上图所示,如果我使用树的深度优先遍历方式来遍历我们的图,我们就会发现,图是具有环状结构的,会产生重复遍历的情况。在对图遍历的时候,我们需要记录,哪个节点被遍历过了。

二:从树的深度优先遍历到图的深度优先遍历

树的深度优先遍历

拿前序遍历来说,树的优先遍历伪码如下:

  1. perirder(root);
  2. preorder(TreeNode node){
  3. if(node != null) {
  4. list.add(node.val);
  5. preorder(node.left);
  6. preorder(node.right);
  7. }
  8. }

图的深度优先遍历

图的深度优先遍历伪码如下,我们会声明一个数组,记录哪个节点已经被遍历过,在逻辑上与树的深度优先遍历其实并没有本质上的不同,只是我们访问了一个节点,就要在声明的数组中,将访问的节点标记为已访问的状态。

  1. visited[0...V-1] = false;
  2. dfs(0);
  3. dfs(int v){
  4. visited[v] = true;
  5. list.add(v);
  6. for(int w : adj(v))
  7. if(!visited[w])
  8. dfs(w);
  9. }

三:DFS 逻辑的微观解读

接下来,我们来具体看一下对于下面个图,深度优先遍历的过程是怎样的?

image.png
我们从 dfs(0) 开始。

image.png

image.png
image.png
image.png
image.png
image.png
image.png
image.png

四:实现图的深度优先遍历

代码链接🔗

代码如下:

  1. visited[0...V] = false;
  2. for(int v = 0; v < V; v++)
  3. if(!visited[v])
  4. dfs(v);
  5. dfs(int v) {
  6. visited[v] = true;
  7. list.add(v);
  8. for(int w : adj(v))
  9. if(!visited[w])
  10. dfs(w);
  11. }

这是深度优先遍历的前序遍历方式,同理,也有深度优先遍历的后续遍历方式:

  1. visited[0...V] = false;
  2. for(int v = 0; v < V; v++)
  3. if(!visited[v])
  4. dfs(v);
  5. dfs(int v) {
  6. visited[v] = true;
  7. for(int w : adj(v))
  8. if(!visited[w])
  9. dfs(w);
  10. list.add(v);
  11. }

五:更多关于图的深度优先遍历

图的深度优先遍历的应用

  • 求图的联通分量
  • 求两点间是否可达
  • 求两点间的一条路径
  • 检测图中是否有环
  • 二分图检测
  • 寻找图中的桥
  • 寻找图中的割点
  • 哈密尔顿路径
  • 拓扑排序
  • … …

非递归方式实现图的深度优先遍历

代码链接🔗

非递归方式实现图的深度优先遍历代码如下:

  1. visited[0...V] = false;
  2. for(int v = 0; v < V; v++)
  3. if(!visited[v])
  4. dfs(v);
  5. dfs(int v) {
  6. Stack<Integer> stack = new Stack<>();
  7. stack.push(v);
  8. visited[v] = true;
  9. while (!stack.isEmpty()) {
  10. int cur = stack.pop();
  11. list.add(cur);
  12. for (int w : G.adj(cur)) {
  13. if (!visited[w]) {
  14. stack.push(w);
  15. visited[w] = true;
  16. }
  17. }
  18. }
  19. }