图的深度优先遍历基本思想:
- 访问指定的起始顶点;
- 若当前访问的顶点的邻接顶点有未被访问的,首先选第一个访问,其次第二个…;反之,退回到最近访问过的顶点,查看当前顶点还有没有其他未访问的邻接顶点,访问之;直到与起始顶点直接或间接相通的全部顶点都访问完毕;
- 若此时图中尚有顶点未被访问(不连通点),则遍历所有顶点作为起始顶点并访问之,转 2; 反之,遍历结束。
算法步骤:
对于上图来说,我们可以将图中各个顶点的连接关系用树来表示(以A为初始顶点):
首先访问A,标记为已访问:
然后访问A的第一个邻接顶点B,如果B没有被访问过,则以B作为新的初始顶点,将B标记为已访问,继续向下查找:
访问B的第一个邻接顶点A,发现已访问过,返回顶点B,接着访问B的第二个邻接节点C,C没有被访问过,所以以C作为初始顶点继续向下查找,并将C标记为已访问:
以C作为初始顶点继续向下查找,发现C的两个邻接顶点都已被访问,返回顶点B,接着访问B的第三个邻接顶点D,以D作为初始顶点继续向下查找,并将D标记为已访问:
以D作为初始顶点继续向下查找,发现D的所有邻接顶点都已被访问,返回顶点B,接着访问B的第四个邻接顶点E,以E作为初始顶点继续向下查找,并将E标记为已访问:
以E作为初始顶点继续向下查找,发现E的所有邻接顶点都已被访问,返回顶点B;此时B的所有邻接顶点也都访问了,返回顶点A,而A的所有邻接顶点也都访问完毕,则程序结束,遍历完毕。
代码实现:
package DataStructure;
import java.util.ArrayList;
import java.util.Arrays;
public class GraphDemo {
public static void main(String[] args) {
Graph graph = new Graph(5);
String[] vertexS = {"A", "B", "C", "D", "E"};
for (String vertex : vertexS) {
graph.insertVertex(vertex);
}
// A-B A-C B-C B-D B-E
graph.insertEdge(0, 1, 1);
graph.insertEdge(0, 2, 1);
graph.insertEdge(1, 2, 1);
graph.insertEdge(1, 3, 1);
graph.insertEdge(1, 4, 1);
graph.showGraph();
System.out.println();
graph.depthFirstSearch();
}
}
class Graph {
private ArrayList<String> vertexList; // 储存顶点
private int[][] edges; // 储存邻接矩阵
private int edgesNum; // 储存边的个数
// 定义一个boolean数组,记录某个顶点是否被访问过,用于遍历
private boolean[] isVisited;
public Graph(int n) {
vertexList = new ArrayList<>(n);
edges = new int[n][n];
isVisited = new boolean[n];
}
// 插入顶点
public void insertVertex(String vertex) {
vertexList.add(vertex);
}
/**
* 添加边
* @param v1 顶点的下标
* @param v2 另一个顶点的下标
* @param flag 1或0 表示直接连接或不直接连接
*/
public void insertEdge(int v1, int v2, int flag) {
edges[v1][v2] = flag;
edges[v2][v1] = flag;
edgesNum++;
}
// 返回下标i对应的顶点数据
public String getVertex(int i) {
return vertexList.get(i);
}
// 显示图对应的矩阵
public void showGraph() {
for (int[] edge : edges) {
System.out.println(Arrays.toString(edge));
}
}
// 获取当前节点的第一个邻接顶点
public int getFirstVertex(int index) {
for (int i = 0; i < vertexList.size(); i++) {
if (edges[index][i] == 1) {
return i;
}
}
return -1;
}
/**
* 获取当前顶点在某个邻接顶点之后的第一个邻接顶点
* @param v1 当前顶点下标
* @param v2 某个邻接顶点的下标
* @return 返回某个邻接顶点之后的第一个邻接顶点的下标,未找到则返回-1
*/
public int getNextVertex(int v1, int v2) {
for (int i = v2 + 1; i < vertexList.size(); i++) {
if (edges[v1][i] == 1) {
return i;
}
}
return -1;
}
// DFS 深度优先遍历:从下标为i的顶点开始,向下纵向遍历
public void depthFirstSearch(int i) {
System.out.println(getVertex(i));
// 设置为已访问
isVisited[i] = true;
// 获取第一个邻接顶点,若没有邻接顶点,说明i是不连通点,则下标为i的顶点的纵向遍历结束
int w = getFirstVertex(i);
while (w != -1) {
if (!isVisited[w]) {
// 如果第一个邻接顶点存在且没有被访问过,递归查找该邻接顶点的第一个邻接顶点
depthFirstSearch(w);
}
// 如果第一个邻接顶点被访问过,继续获取下一个邻接顶点:
w = getNextVertex(i, w);
}
}
// 重载DFS,将不与其他任何顶点联通的顶点遍历出来
public void depthFirstSearch() {
for (int i = 0; i < vertexList.size(); i++) {
if (!isVisited[i]) {
depthFirstSearch(i);
}
}
}
}