Java data structure and algorithm (12): Figure

1. Basic introduction to diagrams

1.1 Why is there a picture?

  • The linear table is limited to the relationship between a direct predecessor and a direct successor;
  • The tree can only have one direct predecessor, which is the parent node;
  • When we need to express a many-to-many relationship, we need to use a graph;

1.2 Illustration of the figure

A graph is a data structure in which a node can have zero or more adjacent elements. The connection between two nodes is called an edge. Nodes can also be called vertices. As shown in the figure:
Insert picture description here

1.3 Common concepts of graphs

  • vertex
  • side
  • path
  • Undirected graph
    Insert picture description here
  • Directed graph
  • Weighted graph
    Insert picture description here

2 Representation of graphs

There are two ways to represent the graph: two-dimensional array representation (leading matrix), linked list representation (leading junction table).

2.1 Leading matrix

The lead matrix is ​​a matrix that represents the relationship between the vertices in the graph. For a graph with n vertices, the matrix is ​​1...n points represented by row and col.
Insert picture description here

2.2 Leading table

  • The leading matrix needs to allocate space for n edges to each vertex. In fact, there are many edges that do not exist, which will cause a certain loss of space;
  • The realization of the link table only cares about the existing edges, not the non-existent edges. Therefore, there is no waste of space, and the leading table is composed of an array + a linked list.
    Insert picture description here

3. How to traverse the graph

The so-called traversal of the graph is the visit to the node. There are generally two traversal methods:

  • Depth-first traversal
  • Breadth first traversal

3.1 The basic idea of ​​depth-first traversal

  • Depth-first traversal, starting from the initial access node, the initial access node may have multiple adjacent nodes. The strategy of depth-first traversal is to first visit the first adjacent node, and then use this visited adjacent node as the initial A node, to visit its first adjacent node, can be understood as follows: After each visit to the current node, the first adjacent node of the current node is first visited;
  • We can see that such an access strategy is to prioritize digging in the vertical direction instead of horizontally accessing all adjacent nodes of a node;
  • Depth-first search is a recursive process;

3.2 Depth-first traversal algorithm steps

  • Visit the initial node v, and mark the node v as visited;
  • Find the first adjacent node w of node v;
  • If w exists, continue execution, if w does not exist, go back to step 1, and continue from the next node of v;
  • If w has not been visited, perform depth-first traversal recursion on w (that is, treat w as another v, and then proceed to steps 1, 2, 3);
  • Find the next adjacent node of the w adjacent node of node v, and go to step 3;
    Insert picture description here

3.3 The basic idea of ​​breadth-first traversal

  • Similar to a hierarchical search process, breadth-first traversal requires the use of a queue to maintain the order of the visited nodes, so that the adjacent nodes of these nodes can be visited in this order;

3.4 Breadth-first traversal algorithm steps

  • Visit the initial node v and mark the node v as visited;
  • Node v enters the queue;
  • When the queue is not empty, continue to execute, otherwise the algorithm ends;
  • Get out of the queue and get the head node u;
  • Find the first adjacent node w of node u;
  • If the adjacent node w of node u does not exist, go to step 3; otherwise, loop the following three steps:
    • If node w has not been visited yet, visit node w and mark it as visited;
    • The node w enters the queue;
    • Find the next adjacent node w of the node u following the adjacent node of w, and go to step 6;

Insert picture description here
Complete code

package com.lele.graph;

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

/**
 * author: hwl
 * date: 2020/12/5 18:52
 * version: 1.0.0
 * modified by:
 * description: 图
 */
public class Graph {
    
    

    private ArrayList<String> vertexList; // 存储顶点集合
    private int[][] edges; // 存储图对应的领接矩阵
    private int numOfEdges; // 表示边的数目
    // 定义给数组boolean[],记录某个节点是否被访问
    private boolean[] isVisited;

    public static void main(String[] args) {
    
    
        // 测试图是否创建ok
        int n = 8;// 结点的个数
//        String Vertexs[] = {"A","B","C","D","E"};
        String Vertexs[] = {
    
    "1","2","3","4","5","6","7","8"};
        // 创建图对象
        Graph graph = new Graph(n);
        // 循环的添加顶点
        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.insertEdge(0,1,1);
        graph.insertEdge(0,2,1);
        graph.insertEdge(1,3,1);
        graph.insertEdge(1,4,1);
        graph.insertEdge(3,7,1);
        graph.insertEdge(4,7,1);
        graph.insertEdge(2,5,1);
        graph.insertEdge(2,6,1);
        graph.insertEdge(5,6,1);

        // 显示领接矩阵
        graph.showGraph();

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

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

    /**
     * 得到第一个邻接结点的下标 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;
    }

    /**
     * 深度优先遍历算法
     * i 第一次就是0
     */
    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);
            }
            // 如果w结点已经被访问过
            w = getNextNeighbor(i, w);
        }
    }

    /**
     * 对dfs进行一个重载,遍历我们所有的结点,并进行dfs
     */
    public void dfs() {
    
    
        isVisited = new boolean[vertexList.size()];
        // 遍历所有的结点,进行dfs[回溯]
        for (int i = 0; i < getNumOfVertex(); i++) {
    
    
            if (!isVisited[i]) {
    
    
                dfs(isVisited, i);
            }
        }
    }

    /**
     * 对一个结点进行广度优先遍历的方法
     * @param isVisited
     * @param i
     */
    private void bfs(boolean[] isVisited, int i) {
    
    
        int u; // 表示队列的头结点对应的下标
        int w; // 邻接结点
        // 队列,记录结点访问的顺序
        LinkedList queue = new LinkedList();
        // 访问结点,输出结点信息
        System.out.print(getValueByIndex(i) + "=>");
        // 标记为已访问
        isVisited[i] = true;
        // 将结点加入队列
        queue.addLast(i);

        while(!queue.isEmpty()) {
    
    
            // 取出队列的头结点下标
            u = (Integer)queue.removeFirst();
            // 获得第一个邻接结点的下标 w
            w = getFirstNeighbor(u);
            while(w != -1) {
    
    // 找到
                // 是否访问过
                if (!isVisited[w]) {
    
    
                    System.out.print(getValueByIndex(w) + "=>");
                    // 标记已经访问
                    isVisited[w] = true;
                    // 入队
                    queue.addLast(w);
                }
                // 以u 为前驱点,找w后面的下一个邻接结点
                w = getNextNeighbor(u, w);// 体现出 广度优先
            }
        }
    }

    /**
     * 遍历所有的结点,都进行广度优先搜索
     */
    public void bfs() {
    
    
        isVisited = new boolean[vertexList.size()];
        for (int i = 0; i < getNumOfVertex(); i++) {
    
    
            if (!isVisited[i]) {
    
    
                bfs(isVisited, i);
            }
        }
    }

    /** 图中 常用的方法 **/
    /**
     * 获取结点的个数
     * @return
     */
    public int getNumOfVertex() {
    
    
        return vertexList.size();
    }

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

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

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

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

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

    /**
     * 添加边
     * @param v1 表示点的下标 即第几个顶点 "A"-"B", "A"->0  "B"->1
     * @param v2 第二个顶点的下标
     * @param weight 表示
     */
    public void insertEdge(int v1, int v2, int weight) {
    
    
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        numOfEdges++;
    }
}

Guess you like

Origin blog.csdn.net/houwanle/article/details/110707754