每天进步一点点【图的深度优先遍历(DFS)】

图是一种数据结构,其中节点可以具有零个或多个相邻元素。两个节点之间的连接称为边。节点也可以称为顶点。

图分为三种:无向图、有向图、带权图

图的表示方式有两种:二维数组表示(邻接矩阵);链表表示(邻接表)

邻接矩阵

邻接矩阵是表示图形中顶点之间相邻关系的矩阵,对于n个顶点的图而言,矩阵的行和列表示的是1……n个点。邻接矩阵需要为每个顶点都分配n个边的空间,其实有很多边都是不存在的,会造成空间的一定损失。

d0195c7dbd8e7e9afd9db11746148f51.png

邻接表

连接表的实现只关心存在的边,不关心不存在的边。因此没有空间浪费,邻接表由数组+链表组成

6397dce6ca85ad39fda43f78ce2b0ff1.png

说明:

  1. 标号为0的节点的相关联的节点为1,2,3,4

  2. 标号为1的节点为0,4

  3. 标号为2的节点的相关联的节点为0,4,5

  4. ……

代码实现图结构
497de495b075b18c15f759877d244f6a.png

/**
 * 邻接矩阵表示图
 * @author sixiaojie
 * @date 2022-01-07-19:05
 */
public class Graph {
    /** 存储顶点集合 */
    private ArrayList<String> vertexList;

    /** 存储图对应的邻接矩阵 */
    private int[][] edges;

    /** 表示边的数目 */
    private int numOfEdges;

    public static void main(String[] args) {
        // 顶点个数
        int n = 5;
        String[] vertexs = {"A","B","C","D","E"};
        Graph graph = new Graph(n);
        // 添加顶点
        for (String vertexValue : vertexs) {
            graph.insertVertex(vertexValue);
        }
        // 添加边 A-B A-C B-C B-D B-E
        graph.insertEdge(0,1,1);// A-B
        graph.insertEdge(0,2,1);// A-C
        graph.insertEdge(1,2,1);// B-C
        graph.insertEdge(1,3,1);// B-D
        graph.insertEdge(1,4,1);// B-E

        // 显示
        graph.showGraph();

    }


    /**
     * 构造器
     */
    public Graph(int n) {
        // 初始化矩阵和vertexList
        edges = new int[n][n];
        vertexList = new ArrayList<String>(n);
        numOfEdges = 0;
    }

    /**
     * 插入顶点
     * @param vertex
     */
    public void insertVertex(String vertex){
        vertexList.add(vertex);
    }

    /**
     * 添加边
     * @param v1 第一个顶点对应的下标
     * @param v2 第二个顶点对应的下标
     * @param weight 权值
     */
    public void insertEdge(int v1,int v2,int weight){
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        numOfEdges ++ ;
    }

    /**
     * 返回顶点个数
     * @return
     */
    public int getNumOfVertex(){
        return vertexList.size();
    }

    /**
     * 返回边的数目
     * @return
     */
    public int getNumOfEdges(){
        return numOfEdges;
    }

    /**
     * 返回顶点i(下标)对应的数据0->'A',1->'B',2->'C'
     * @param index
     * @return
     */
    public String getValueByIndex(int index){
        return vertexList.get(index);
    }

    /**
     * 返回v1,v2的权值
     * @param v1
     * @param v2
     * @return
     */
    public int getWeiht(int v1, int v2){
        return edges[v1][v2];
    }

    /**
     * 显示对应的矩阵
     */
    public void showGraph(){
        for (int[] link : edges) {
            System.out.println(Arrays.toString(link));
        }

    }
}
图的深度优先(DFS)遍历
  1. 深度优先遍历的策略就是首先访问第一个邻接节点,然后再以这个被访问的邻接节点作为初始节点,访问它的第一个邻接节点,可以这样理解:每次都在访问完当前节点后首先访问当前节点的第一个邻接节点。

  2. 我们可以看出,这样的访问策略是优先往纵向挖掘深入,而不是对一个节点的所有邻接节点进行横向访问。

  3. 显然,深度优先搜索是一个递归的过程

深度优先遍历算法步骤

  1. 访问初始节点v,并标记节点v为已经访问

  2. 查找节点v的第一个邻接节点w

  3. 若w存在,则继续执行4,如果w不存在,则回到第1步,将从v的下一个节点继续

  4. 若w未被访问,对w进行深度优先遍历递归(即把w当作另一个v,然后进行步骤123)

  5. 若w已经被访问,查找节点v的w邻接节点的下一个邻接节点,转到步骤3

代码实现:

/**
 * 邻接矩阵 深度优先遍历
 * @author sixiaojie
 * @date 2022-01-07-19:05
 */
public class Graph {
    /** 存储顶点集合 */
    private ArrayList<String> vertexList;

    /** 存储图对应的邻接矩阵 */
    private int[][] edges;

    /** 表示边的数目 */
    private int numOfEdges;

    /** 记录某个节点是否被访问 */
    private boolean[] isVisited;

    public static void main(String[] args) {
        // 顶点个数
        int n = 5;
        String[] vertexs = {"A","B","C","D","E"};
        Graph graph = new Graph(n);
        // 添加顶点
        for (String vertexValue : vertexs) {
            graph.insertVertex(vertexValue);
        }
        // 添加边 A-B A-C B-C B-D B-E
        graph.insertEdge(0,1,1);// A-B
        graph.insertEdge(0,2,1);// A-C
        graph.insertEdge(1,2,1);// B-C
        graph.insertEdge(1,3,1);// B-D
        graph.insertEdge(1,4,1);// B-E

        // 显示
        graph.showGraph();

        // 测试深度优先遍历
        System.out.println("深度优先遍历");
        graph.dfs();
    }


    /**
     * 构造器
     */
    public Graph(int n) {
        // 初始化矩阵和vertexList
        edges = new int[n][n];
        vertexList = new ArrayList<String>(n);
        numOfEdges = 0;
        isVisited = new boolean[n];
    }

    /**
     * 插入顶点
     * @param vertex
     */
    public void insertVertex(String vertex){
        vertexList.add(vertex);
    }

    /**
     * 添加边
     * @param v1 第一个顶点对应的下标
     * @param v2 第二个顶点对应的下标
     * @param weight 权值
     */
    public void insertEdge(int v1,int v2,int weight){
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        numOfEdges ++ ;
    }

    /**
     * 返回顶点个数
     * @return
     */
    public int getNumOfVertex(){
        return vertexList.size();
    }

    /**
     * 返回边的数目
     * @return
     */
    public int getNumOfEdges(){
        return numOfEdges;
    }

    /**
     * 返回顶点i(下标)对应的数据0->'A',1->'B',2->'C'
     * @param index
     * @return
     */
    public String getValueByIndex(int index){
        return vertexList.get(index);
    }

    /**
     * 返回v1,v2的权值
     * @param v1
     * @param v2
     * @return
     */
    public int getWeiht(int v1, int v2){
        return edges[v1][v2];
    }

    /**
     * 显示对应的矩阵
     */
    public void showGraph(){
        for (int[] link : edges) {
            System.out.println(Arrays.toString(link));
        }

    }

    /**
     * 获取第一个邻接节点的下标w
     * @param index
     * @return 如果存在就返回对应的下标,不存在返回-1
     */
    public int getFirstNeighbor(int index){
        for (int j = 0; j < vertexList.size(); j++) {
            if (edges[index][j] > 0){
                return j;
            }
        }
        return -1;
    }

    /**
     * 根据前一个邻接节点的下标来获取下一个邻接节点
     * @param v1
     * @param v2
     * @return
     */
    public int getNextNeighbor(int v1,int v2){
        for (int j = v2 + 1; j < vertexList.size()  ; j++) {
            if(edges[v1][j] > 0){
                return j;
            }
        }
        return -1;
    }

    /**
     * 深度优先遍历
     * @param isVisited
     * @param i
     */
    public void dfs(boolean[] isVisited,int i){
        // 首先访问该节点,输出
        System.out.print(getValueByIndex(i) + "->");
        // 将该节点设置为已经访问
        isVisited[i] = true;
        // 查找节点i的第一个邻接节点w
        int w = getFirstNeighbor(i);

        while (w != -1){
            // 没有被访问过
            if(!isVisited[w]){
                dfs(isVisited,w);
            }
            // 已经被访问过,查找节点i的w邻接节点的下一个邻接节点
            w = getNextNeighbor(i,w);
        }
    }

    /**
     * 遍历所有的节点,并进行dfs
     */
    public void dfs(){
        // 遍历所有的节点,进行dfs[回溯]
        for (int i = 0; i < getNumOfVertex(); i++) {
            if(!isVisited[i]){
                dfs(isVisited,i);
            }
        }
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
深度优先遍历DFS)是一种用于遍历或搜索树或算法。该算法从根节点开始,尽可能深地访问每个节点,直到到达叶子节点为止。然后回溯到上一个节点,继续访问其他节点,直到遍历完整个树或。 以下是深度优先遍历的伪代码: ``` DFS(G, v): visited[v] = true for each w in G.adjacent(v) do if not visited[w] then DFS(G, w) ``` 其中,G表示,v表示起始节点,visited表示节点是否被访问过的标记。 以为例,假设从节点A开始进行深度优先遍历,遍历过程如下: 1.访问节点A,将visited[A]标记为true。 2.从节点A出发,访问其邻接节点B,将visited[B]标记为true。 3.从节点B出发,访问其邻接节点D,将visited[D]标记为true。 4.从节点D出发,发现没有未访问的邻接节点,回溯到节点B。 5.从节点B出发,访问其邻接节点E,将visited[E]标记为true。 6.从节点E出发,访问其邻接节点G,将visited[G]标记为true。 7.从节点G出发,发现没有未访问的邻接节点,回溯到节点E。 8.从节点E出发,发现没有未访问的邻接节点,回溯到节点B。 9.从节点B出发,发现没有未访问的邻接节点,回溯到节点A。 10.从节点A出发,访问其邻接节点C,将visited[C]标记为true。 11.从节点C出发,访问其邻接节点F,将visited[F]标记为true。 12.从节点F出发,发现没有未访问的邻接节点,回溯到节点C。 13.从节点C出发,发现没有未访问的邻接节点,回溯到节点A。 14.遍历结束。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值