图的深度优先遍历(Depth First Search)
基本思想
类似于二叉树的先序遍历
-
假设图中所有结点均未被访问,从初始结点访问,访问其第一个邻接结点,接着以被访问的邻接结点作为初始结点,访问它的第一个邻接结点。
-
递归的过程。
算法步骤
-
访问初始结点v,并标记为已访问
-
查找结点v的第一个邻接结点w
-
若w存在,则继续执行4,若不存在则回到第1步,从v的下一个结点继续
-
若w未被访问,对w进行深度优先遍历递归(即把w当做另一个v,然后进行步骤123)
-
查找结点v的邻接结点w的下一个邻接结点,转到步骤3
无向图深度优先遍历演示
1. 访问A
2. 访问(A的邻接点)C
在第1步访问A之后,接下来应该访问的是A的邻接点,即"C,D,F"中的一个。但在本文的实现中,顶点ABCDEFG是按照顺序存储,C在"D和F"的前面,因此,先访问C。
3. 访问(C的邻接点)B
在第2步访问C之后,接下来应该访问C的邻接点,即"B和D"中一个(A已经被访问过,就不算在内)。而由于B在D之前,先访问B。
4. 访问(C的邻接点)D
在第3步访问了C的邻接点B之后,B没有未被访问的邻接点;因此,返回到访问C的另一个邻接点D。
5. 访问(A的邻接点)F
前面已经访问了A,并且访问完了"A的邻接点C的所有邻接点(包括递归的邻接点在内)";因此,此时返回到访问A的另一个邻接点F。
6. 访问(F的邻接点)G
7. 访问(G的邻接点)E
因此访问顺序是:A -> C -> B -> D -> F -> G -> E
有向图深度优先遍历演示
1. 访问A
2. 访问B
在访问了A之后,接下来应该访问的是A的出边的另一个顶点,即顶点B。
3. 访问C
在访问了B之后,接下来应该访问的是B的出边的另一个顶点,即顶点C,E,F。在本文实现的图中,顶点ABCDEFG按照顺序存储,因此先访问C。
4. 访问E
接下来访问C的出边的另一个顶点,即顶点E。
5. 访问D
接下来访问E的出边的另一个顶点,即顶点B,D。顶点B已经被访问过,因此访问顶点D。
6. 访问F
访问D之后,出边顶点C已访问;回溯到E,E的出边顶点BD均访问;依次回溯到B,B的出边顶点CF中F未访问,所以访问F。
7. 访问G
因此访问顺序是:A -> B -> C -> E -> D -> F -> G
算法实现
import java.util.ArrayList;
import java.util.Arrays;
public class GraphTraversal {
private ArrayList<Object> nodeList;//存储结点的集合
private int[][] edges;//结点与结点之间的连线关系
private int numOfEdges;//图中连线数量
private boolean[] isVisited;//标记结点是否被访问
public GraphTraversal(int n){
//初始化矩阵和顶点列表
nodeList = new ArrayList<Object>(n);
edges = new int[n][n];
numOfEdges = 0;
isVisited = new boolean[n];
}
public static void main(String[] args) {
//声明图中结点
int n = 7;
String nodes[] = {"A","B","C","D","E","F","G"};
//创建一个无向图
GraphTraversal undirectedGraph = new GraphTraversal(n);
//将顶点添加到图中
if(undirectedGraph.addNodes(nodes) > 0){
//添加顶点间的关系
undirectedGraph.insertUndirectedEdge(0, 3, 1);
undirectedGraph.insertUndirectedEdge(0, 2, 1);
undirectedGraph.insertUndirectedEdge(0, 5, 1);
undirectedGraph.insertUndirectedEdge(2, 1, 1);
undirectedGraph.insertUndirectedEdge(2, 3, 1);
undirectedGraph.insertUndirectedEdge(5, 6, 1);
undirectedGraph.insertUndirectedEdge(6, 4, 1);
//显示邻接矩阵
System.out.println("无向图邻接矩阵:");
undirectedGraph.showGraph();
//无向图深度优先遍历
System.out.println("无向图深度优先遍历:");
undirectedGraph.dfs();
}
//创建一个有向图
GraphTraversal directedGraph = new GraphTraversal(n);
//添加结点到图中
if (directedGraph.addNodes(nodes) > 0){
//添加结点之间的关系
directedGraph.insertDirectedEdge(0,1,1);
directedGraph.insertDirectedEdge(1,2,1);
directedGraph.insertDirectedEdge(1,4,1);
directedGraph.insertDirectedEdge(1,5,1);
directedGraph.insertDirectedEdge(2,4,1);
directedGraph.insertDirectedEdge(3,2,1);
directedGraph.insertDirectedEdge(4,3,1);
directedGraph.insertDirectedEdge(4,1,1);
directedGraph.insertDirectedEdge(5,6,1);
//显示邻接矩阵
System.out.println("有向图邻接矩阵:");
directedGraph.showGraph();
//有向图深度优先遍历
System.out.println("有向图深度优先遍历:");
directedGraph.dfs();
}
}
//将结点添加到图中
public int addNodes(Object[] nodes){
for (Object node : nodes){
nodeList.add(node);
}
return nodeList.size();
}
//创建无向图结点关系
public void insertUndirectedEdge(int v1, int v2, int weight){
edges[v1][v2] = weight;
edges[v2][v1] = weight;
numOfEdges++;
}
//创建有向图结点关系
public void insertDirectedEdge(int v1, int v2, int weight){
edges[v1][v2] = weight;
numOfEdges++;
}
//显示邻接矩阵
public void showGraph(){
for (int[] link : edges){
System.out.println(Arrays.toString(link));
}
}
//根据初始结点获取第一个邻接结点
public int getFirstNeighbor(int v1){
for(int v2 = 0; v2 < edges.length; v2++){
if (edges[v1][v2] > 0){
return v2;
}
}
return -1;
}
//根据前一个邻接结点获取下一个邻接结点
public int getNextNeighbor(int v1, int v2){
for (int j = v2 + 1; j < edges.length; j++){//从上一个结点[v1]开始找下一个结点
if (edges[v1][j] > 0){//若 v1 到 j 的连线存在,则存在下一个结点
return j;
}
}
return -1;//说明不存在下一个结点
}
//dfs深度优先遍历
public void dfs(boolean[] isVisited, int v){
//输出当前访问的起始结点
System.out.print(nodeList.get(v) + "->");//根据索引值获取List中结点值
//当前起始结点标记为已访问
isVisited[v] = true;
//访问起始结点的第一个邻接结点
int w = getFirstNeighbor(v);
while (w != -1){//说明存在第一个邻接结点
if (!isVisited[w]){//未被标记为已访问
dfs(isVisited, w);//把当前的邻接结点作为起始结点,递归执行深度遍历算法
}
//如果w结点已经被访问,则寻找下一个邻接结点
w = getNextNeighbor(v, w);
}
}
//对 dfs 重载,使其对所有结点进行 dfs
public void dfs(){
for (int i = 0; i < nodeList.size(); i++){
if (!isVisited[i]){//结点未被标记为已访问
dfs(isVisited, i);//第 i 个结点作为初始结点进行 dfs
}
}
System.out.println();
}
}