第十三章:图

13.4图的深度优先遍历

所谓图的遍历,即是对结点的访问。一个图有那么多个结点,如何遍历这些结点,需要特定策略,一般有两种访问策略:
1、 深度优先遍历
2、 广度优先遍历

图的深度首先遍历思路:
1、 深度优先遍历,从初始访问结点出发,初始访问结点可能有多个邻接结点,深度优先遍历的策略就是首先访问第一个邻接结点,然后再以这个被访问的邻接结点作为初始结点,访问它的第一个邻接结点,可以这样理解: 每次都在访问完当前结点后首先访问当前结点的第一个邻接结点
2、 这样的访问策略是优先往纵向挖掘深入,而不是对一个结点的所有邻接结点进行横向访问
3、 深度优先搜索是一个递归的过程

深度优先遍历算法步骤:
1、 访问初始结点 v,并标记结点 v 为已访问。
2、 查找结点 v 的第一个邻接节点w
3、 若 w 存在,则继续执行 4,如果 w 不存在,则回到第 1 步,将从 v 的下一个结点继续
4、 若 w 未被访问,对 w 进行深度优先遍历递归(即把 w 当做另一个 v,然后进行步骤 123)
5、 查找结点 v 的 w 邻接结点的下一个邻接结点,转到步骤 3

13.5图的广度优先遍历

图的广度优先搜索(Broad First Search):类似于一个分层搜索的过程,广度优先遍历需要使用一个队列以保持访问过的结点的顺序,以便按这个顺序来访问这些结点的邻接结点

广度优先遍历算法步骤:
1、 访问初始结点 v 并标记结点 v 为已访问
2、 结点 v 入队列
3、 当队列非空时,继续执行,否则算法结束
4、 出队列,取得队头结点 u
5、 查找结点 u 的第一个邻接结点 w
6、 若结点 u 的邻接结点 w 不存在,则转到步骤 3;否则循环执行以下三个步骤:
(1) 若结点 w 尚未被访问,则访问结点 w 并标记为已访问
(2) 结点 w 入队列
(3) 查找结点 u 的继 w 邻接结点后的下一个邻接结点 w,转到步骤 6

13.8图的深度优先VS广度优先

两种遍历方法进行遍历的结果:
在这里插入图片描述

package com.atguigu13.graph;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;

/**
 * @author peng
 * @date 2021/12/3 - 9:08
 *
 * 图的的实现
 */
public class Graph {
    private ArrayList<String> vertexList;//使用ArrayList来存储图的顶点
    private int[][] edges;//用于存储图的邻接矩阵
    private int numberOfEdges;//用于存储图的边的个数
    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 vertex : vertexs) {
            graph.insertVertex(vertex);
        }
        //添加对应的边
        graph.insertEdges(0,1,1);
        graph.insertEdges(0,2,1);
        graph.insertEdges(1,2,1);
        graph.insertEdges(1,3,1);
        graph.insertEdges(1,4,1);
        //打印矩阵
        graph.showGraph();

//        //测试图的深度优先遍历
//        System.out.println("图的深度优先遍历算法:");
//        graph.dfs();

        //测试图的广度优先搜索
        System.out.println();
        System.out.println("图的广度优先搜索:");
        graph.dfs();
    }

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

    /**
     * 实现插入节点
     */
    public void insertVertex(String vertex) {
        //直接将新的节点插入到ArrayList中即可
        vertexList.add(vertex);
    }

    /**
     * 实现添加边的操作:v1、v2分别表示添加的边的两个顶点对应的坐标,weight表示该边是连接的还是不连接的,0表示不连接(默认是0,1表示连接)
     */
    public void insertEdges(int v1, int v2, int weight) {
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        numberOfEdges++;
    }

    /**
     * 实现返回图中节点的个数:直接返回ArrayListz中元素的个数即可
     */
    public int getNumberOfVertex() {
        return vertexList.size();
    }

    /**
     * 返回图中的边的个数
     */
    public int getNumberOfEdges() {
        return numberOfEdges;
    }

    /**
     * 返回索引对应的元素:0--->A, 1--->B
     */
    public String getValueByIndex(int index) {
        return vertexList.get(index);
    }

    /**
     * 返回两个点之间的权值
     */
    public int getWeight(int v1, int v2) {
        return edges[v1][v2];
    }

    /**
     * 显示邻接矩阵
     */
    public void showGraph() {
        for (int[] link : edges) {
            //这是遍历矩阵的一个新方法:将矩阵一行一行进行输出
            System.out.println(Arrays.toString(link));
        }
    }

    //以下的方法是实现图的深度优先遍历算法
    /**
     * 返回当前节点的下一个节点的下标(下标是相对于ArrayList来说的,坐标是相对于邻接矩阵来说的),如果有则返回对应的下标,没有则返回-1
     */
    public int getFirstNeighbor(int index) {
        for (int i = 0; i < vertexList.size(); i++) {
            //假设现在要找到A节点的连接的下一个节点是谁,以A节点为行,遍历寻找BCDE节点,看A节点与哪一个节点之间存在边,如果存在边表示
            //这两个节点是相连的,就直接返回对应节点的下标,如果没有>0表示,这两个节点之间没有边,返回-1
            if (edges[index][i] > 0) {
                return i;
            }
        }
        return -1;
    }

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

    /**
     * 实现深度优先遍历算法
     */
    public void dfs(boolean[] isVisited, int v1) {
        //第一步:遍历图的第一个节点,并标记为已遍历
        System.out.print(getValueByIndex(v1) + "--->");
        isVisited[v1] = true;
        //第二步:找到v1节点的第一个邻接节点
        int v2 = getFirstNeighbor(v1);
        while (v2 != -1) {//表示存在该节点
            //先判断有没有被访问过
            if (isVisited[v2]) {
                //如果该节点已经被访问过了,就继续寻找下一个节点
                /**
                 * 在这里我们可以看到getFirstNeighbor和getNextNeighbor的区别
                 * getFirstNeighbor:是找到当前节点的第一个邻接节点
                 * getNextNeighbor:是找当前节点的邻接节点
                 * 两个方法在实质上是一样的,只不是将一个方法拆开了两个,这样使用起来的话会更加方便
                 */
                v2 = getNextNeighbor(v1, v2);
            }else {
                //如果找的这个节点没有被访问过,利用递归以该节点为起点进行遍历
                dfs(isVisited, v2);
            }
        }
    }

    /**
     * 对dfs进行重载:对所有的节点进行dfs
     */
    public void dfs() {
        for (int i = 0; i < getNumberOfVertex(); i++) {
            if (!isVisited[i]) {
                //如果当前节点还没有被访问过才能进行dfs
                dfs(isVisited, i);
            }
        }
    }

    /**
     * 对图进行广度优先遍历
     */
    public void bfs(boolean[] isVisited, int v1) {
        int u;//队列头节点对应的下标
        int v;//邻接节点
        //创建队列,记录节点访问的顺序
        LinkedList queue = new LinkedList();
        //访问节点,输出节点的信息
        System.out.print(getValueByIndex(v1) + "--->");
        //标记节点已经访问
        isVisited[v1] = true;
        //将节点加入队列
        queue.addLast(v1);
        while (!queue.isEmpty()) {
            //如果当前队列是非空的
            u = (Integer)queue.removeFirst();//取出队列的头节点进行遍历
            v = getFirstNeighbor(u);//找到当前队列头的第一个邻接子节点
            if (v != -1) {
                //说明当前的邻接子点是存在的
                if (!isVisited[v]) {
                    //如果当前的节点没有访问过
                    System.out.print(getValueByIndex(v) + "--->");//输出当前节点
                    isVisited[v] = true;
                    //入队
                    queue.addLast(v);
                }
                //如果这个邻接节点节点是已经访问过的了,那么就找当前节点的下一个邻接节点
                v = getNextNeighbor(u, v);
            }
        }
    }

    /**
     * 遍历所有的节点,都进行广度优先搜索
     */
    public void bfs() {
        for (int i = 0; i < getNumberOfVertex(); i++) {
            if (!isVisited[i]){
                //如果当前节点没有被访问过,就进行广度优先搜索
                bfs(isVisited, i);
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值