无向图的邻接矩阵建立及DFS和BFS遍历

一.图的定义

定义:图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。

图的分类:

图是按照无方向和有方向分为无向图和有向图。

无向图的邻接矩阵建立及DFS和BFS遍历

左图为无向图是由顶点和边构成,右图为有向图是由顶点和弧(有向边构成)。弧有弧头和弧尾区别

二、图的存储结构

1、邻接矩阵

邻接矩阵用两个数组保存数据。一个一维数组存储图中顶点信息,一个二维数组存储图中边或弧的信息。

无向图的邻接矩阵建立及DFS和BFS遍历

2、邻接表

邻接表:数组和链表相结合的存储方法为邻接表。

无向图的邻接矩阵建立及DFS和BFS遍历

三.邻接矩阵的实现

public class Graph {
    private ArrayList<String> vertexList;//存储顶点集合
    private int[][] edges; //存储图对应的邻接矩阵
    private int numOfEdge; //表示边的数目
    private boolean[] isVisited;

    public static void main(String[] args) {
        int n = 5;
        Graph graph = new Graph(n);
        String[] vertexValue = { "A", "B", "C", "D", "E" };
        // 建立各顶点所对应的值
        for (String vertex : vertexValue) {
            graph.insertVertex(vertex);
        }
        // 构造边的关系
        graph.insertEdges(0, 0, 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.bfs();
    }

    /**
     *
     * @param //顶点个数
     */
    public Graph(int n) {
        edges = new int[n][n];
        vertexList = new ArrayList<String>(n);
    }

    /**
     *
     * @Title: insertVertex
     * @Description: 向顶点中加入该顶点的数据
     * @param @param vertex 要插入的数据
     * @return void 返回类型
     */
    public void insertVertex(String vertex) {
        vertexList.add(vertex);
    }

    /**
     *
     * @Title: insertEdges
     * @Description: 将邻接矩阵各个结点之间的关系建立起来,1代表相连,0代表不相连
     * @param @param v1 代表下标为v1的顶点
     * @param @param v2 代表下标为v2的顶点
     * @param @param weight 权值,不是0就是1
     * @return void 返回类型
     */
    public void insertEdges(int v1, int v2, int weight) {
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        numOfEdge++;
    }

    // 返回结点数
    public int getNumOfVertex() {
        return vertexList.size();
    }

    // 返回边数
    public int getNumOfEdges() {
        return numOfEdge;
    }

    // 返回i对应的数据
    public String getValueByyIndex(int i) {
        return vertexList.get(i);
    }

    // 返回v1和v2的权值
    public int getWeight(int v1, int v2) {
        return edges[v1][v2];
    }

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

四.图的深度优先遍历

深度优先遍历(Depth-First-Search),从初始访问结点出发,初始访问结点可能有多个邻接结点,深度优先遍历的策略就是首先访问,第一个邻接结点,然后再以这个被访问的邻接结点作为初始结点,访问它的第一个邻接结点, 可以这样理解每次都在访问完当前结点后首先访问当前结点的第一个邻接结点。

深度优先搜索是一个递归的过程。

无向图的邻接矩阵建立及DFS和BFS遍历

1. 深度优先遍历算法步骤

1、访问初始顶点x,访问后需要标记x已经访问过,不能再次重读访问

2、查找顶点x的第一个邻接点y

3、如果y存在,则继续执行下面步骤,如果y不存在,则回到第1步,将从x的下一个顶点继续

4、如果y未被访问过,对y进行深度优先遍历,则是把y当做另一个x,然后执行123步

5、查找顶点x的y邻接点的下一个邻接点,转到步骤3

 //得到第一个临结节点的下标w
    //如果存在就返回对应的下标
    public int getFirstNeighbor(int index){
        for (int i = 0; i < vertexList.size(); i++) {
            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;
    }

    //对dfs重载,遍历所有的节点
    public void dfs(){
        isVisited = new boolean[5];
        //遍历所有节点进行dfs[回溯]
        for (int i = 0; i < getNumOfVertex(); i++) {
            if (!isVisited[i]){
                dfs(isVisited,i);
            }
        }
    }
    //深度优先遍历
    public void dfs(boolean[] isVisited,int i){
    //首先访问该节点
        System.out.print(getValueByyIndex(i)+"->");
        //将该节点设置为已访问
        isVisited[i] = true;
        //查找节点i的第一个邻接节点w
        int w =getFirstNeighbor(i);
        while (w!=-1){
            if (!isVisited[w]){
                dfs(isVisited,w);
            }
            //如果w节点被访问过
            w = getNextNeighbor(i,w);
        }
    }

五.广度优先遍历

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

1 广度优先遍历算法分析

1)访问初始结点 v 并标记结点 v 为已访问。

  1. 结点 v 入队列

  2. 当队列非空时,继续执行,否则算法结束。

  3. 出队列,取得队头结点 u。

  4. 查找结点 u 的第一个邻接结点 w。

  5. 若结点 u 的邻接结点 w 不存在,则转到步骤 3;否则循环执行以下三个步骤

6.1若结点 w 尚未被访问,则访问结点 w 并标记为已访问。

6.2 结点 w 入队列

6.3 查找结点 u 的继 w 邻接结点后的下一个邻接结点 w,转到步骤 6

   //遍历所有节点
    public void bfs() {
        isVisited = new boolean[5];
        //遍历所有节点进行bfs
        for (int i = 0; i < getNumOfVertex(); i++) {
            if (!isVisited[i]) {
                bfs(isVisited, i);
            }
        }
    }
    //进行广度优先遍历
    private void bfs(boolean[] isVisited, int i) {
        int u; //表示队列头节点对应下标
        int w; //邻接节点
        //队列,记录节点访问的顺序
        LinkedList list = new LinkedList();
        //访问节点 输出节点信息
        System.out.print(getValueByyIndex(i) + "->");
        //标记为已访问
        isVisited[i] = true;
        //将节点加入队列
        list.addLast(i);
        while (!list.isEmpty()) {
            //取出队列头结点下标
            u = (Integer) list.removeFirst();
            //得到第一个临界点的下标
            w = getFirstNeighbor(u);
            while (w != -1) {
                if (!isVisited[w]) {
                    System.out.print(getValueByyIndex(w) + "->");
                    //标记为已访问
                    isVisited[w] = true;
                    //入队
                    list.addLast(w);
                }
                //以u找w后面的下一个邻接点
                w = getNextNeighbor(u, w);
            }
        }
    }

上一篇:python日常案例汇集


下一篇:LeetCode刷题笔记 字节每日打卡 被围绕的区域