图的深度优先遍历基本思想:

  1. 访问指定的起始顶点;
  2. 若当前访问的顶点的邻接顶点有未被访问的,首先选第一个访问,其次第二个…;反之,退回到最近访问过的顶点,查看当前顶点还有没有其他未访问的邻接顶点,访问之;直到与起始顶点直接或间接相通的全部顶点都访问完毕;
  3. 若此时图中尚有顶点未被访问(不连通点),则遍历所有顶点作为起始顶点并访问之,转 2; 反之,遍历结束。

算法步骤:

图的深度优先遍历 邻接矩阵 python 图的深度优先遍历例题_List


对于上图来说,我们可以将图中各个顶点的连接关系用树来表示(以A为初始顶点):

首先访问A,标记为已访问:

图的深度优先遍历 邻接矩阵 python 图的深度优先遍历例题_Graph_02

然后访问A的第一个邻接顶点B,如果B没有被访问过,则以B作为新的初始顶点,将B标记为已访问,继续向下查找:

图的深度优先遍历 邻接矩阵 python 图的深度优先遍历例题_List_03

访问B的第一个邻接顶点A,发现已访问过,返回顶点B,接着访问B的第二个邻接节点C,C没有被访问过,所以以C作为初始顶点继续向下查找,并将C标记为已访问:

图的深度优先遍历 邻接矩阵 python 图的深度优先遍历例题_System_04


以C作为初始顶点继续向下查找,发现C的两个邻接顶点都已被访问,返回顶点B,接着访问B的第三个邻接顶点D,以D作为初始顶点继续向下查找,并将D标记为已访问:

图的深度优先遍历 邻接矩阵 python 图的深度优先遍历例题_System_05


以D作为初始顶点继续向下查找,发现D的所有邻接顶点都已被访问,返回顶点B,接着访问B的第四个邻接顶点E,以E作为初始顶点继续向下查找,并将E标记为已访问:

图的深度优先遍历 邻接矩阵 python 图的深度优先遍历例题_Graph_06


以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);
            }
        }
    }
}