Data structure (16)-figure

Preface

  We introduced the concept of trees and related applications of trees through several articles. This article introduces you to the last data structure in the data structure-graph. This article introduces the knowledge of graphs from the perspectives of the concept of graphs and the two traversals of graphs. First, we introduce the basic concepts related to graphs.

First, the basic introduction of the picture

1. The meaning of the picture

  Earlier we introduced trees and linear tables. The limitation of linear tables is that there is only a direct predecessor and a direct successor relationship. Secondly, the tree can only have one direct predecessor, that is, the parent node. When we need to express the many-to-many relationship, the data structures we learned earlier cannot be satisfied at this time, and we can only use the graph we introduced today. deal with. A graph is a data structure in which nodes can have zero or more adjacent elements. The connection between two nodes is called an edge, and the node is also called a vertex, as shown in the following figure:

2. Common concepts of graphs

  Commonly used conceptual diagram has the vertices, edges, path, undirected (without direction connection between the vertices, for example AB, may be a A-> B, may be B-> A), particularly as shown below:

  There Graphs (the connection between vertices is directional, such as AB, can only be A->B, not B->A), weighted graphs (this kind of sideband weight graph is also called net), specific As shown in the figure below:

Second, the representation of the graph

  There are two ways to represent graphs: two-dimensional array representation, also called adjacency matrix; linked list representation, namely: adjacency list.
  Adjacency matrix is a matrix showing the relationship between the pattern of adjacent vertices, for n FIG vertices, the matrix is represented by a row and col. 1 ...... n points are shown as follows:

  o access matrix need Each vertex is allocated a space of n edges . In fact, there are many edges that do not exist, which will cause a certain loss of space. And achieved only concerned about the presence of the adjacent table edge , the edges do not care do not exist, and therefore, there is no wasted space, adjacent table lists the composition of an array + , as shown below:

  However, It should be noted that:

  1. The associated node of the node labeled 0 is 1 2 3 4
  2. The associated node of the node labeled 1 is 0 4
  3. The associated node of the node labeled 2 0 4 5
  Next, and so on...

Third, the traversal of the graph

  The so-called graph traversal is the visit to the node. A graph has so many nodes. How to traverse these nodes requires a specific strategy. Generally, there are two access strategies. Depth-first traversal and breadth-first traversal. First, I will introduce you to the depth-first traversal of the graph.

1. Depth-first traversal of graphs

  The basic idea of ​​depth-first traversal is as follows:

  1. 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 adjacent node to be visited. As the initial node, visit its first adjacent node, we can understand the above process like this: each time after visiting the current node, first visit the first adjacent node of the current node.
  2. From the above process, it can be seen that such an access strategy is to dig in the vertical direction first, instead of horizontally accessing all adjacent nodes of a node. Obviously, this is a recursive process.

  Based on the above description of the idea of ​​depth-first traversal of graphs, the following describes the specific steps of depth-first traversal:

  • 1. Visit the initial node v and mark that node v has been visited.
  • 2. Find the first adjacent node w of node v.
  • 3. If w exists, continue to execute 4, if w does not exist, go back to step 1, and continue from the next node of v
  • 4. 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
  • 5. Find the next adjacent node of the w adjacent node of node v, and go to step 3

  The schematic diagram of the specific analysis is shown in the figure:


  0 means that it cannot be directly connected, and 1 means that it can be directly connected.

2. The breadth of the graph is first traversed

  The breadth-first traversal of the graph is similar to a hierarchical search process. The breadth-first traversal needs to use a queue to maintain the order of the visited nodes, so that the adjacent nodes of these nodes can be visited in this order. The specific traversal steps are as follows:

  • 1. Visit the initial node v and mark that v has been visited
  • 2. Node v enters the queue
  • 3. When the queue is not empty, continue execution, otherwise the algorithm ends
  • 4. Get out of the queue and get the head node u
  • 5. Find the first adjacent node w of node u
  • 6. If the adjacent node w of node u does not exist, go to step 3; otherwise, continue to perform the following three steps in a loop

  6.1. If the node w has not been visited yet, then visit the node w and mark the visited
  6.2, the node w enters the queue
  6.3, find the next adjacent node w following the adjacent node w of the node u, and go to step 6

  The results of the specific conversion are as follows:

  Next, we use java to implement these two traversals

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

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

        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遍历是否ok
        System.out.println("深度遍历");
        graph.dfs(); // A->B->C->D->E [1->2->4->8->5->3->6->7]
//		System.out.println();
        System.out.println("广度优先!");
        graph.bfs(); // A->B->C->D-E [1->2->3->4->5->6->7->8]

    }

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

    }

    public int getFirstNeighbor(int index) {
    
    
        for(int j = 0; j < vertexList.size(); j++) {
    
    
            if(edges[index][j] > 0) {
    
    
                return j;
            }
        }
        return -1;
    }
    //根据前一个邻接结点的下标来获取下一个邻接结点
    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
    private 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);
            }
        }
    }

    //对一个结点进行广度优先遍历的方法
    private void bfs(boolean[] isVisited, int i) {
    
    
        int u ; // 表示队列的头结点对应下标
        int w ; // 邻接结点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);
            }
        }
    }

    //图中常用的方法
    //返回结点的个数
    public int getNumOfVertex() {
    
    
        return vertexList.size();
    }
    //显示图对应的矩阵
    public void showGraph() {
    
    
        for(int[] link : edges) {
    
    
            System.err.println(Arrays.toString(link));
        }
    }
    //得到边的数目
    public int getNumOfEdges() {
    
    
        return numOfEdges;
    }
    //返回结点i(下标)对应的数据 0->"A" 1->"B" 2->"C"
    public String getValueByIndex(int i) {
    
    
        return vertexList.get(i);
    }
    //返回v1和v2的权值
    public int getWeight(int v1, int v2) {
    
    
        return edges[v1][v2];
    }
    //插入结点
    public void insertVertex(String vertex) {
    
    
        vertexList.add(vertex);
    }

    public void insertEdge(int v1, int v2, int weight) {
    
    
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        numOfEdges++;
    }
}

  The specific execution results are shown in the figure.

  Finally, we take the following figure as an example to give the results of two priority traversals, namely, breadth and depth, so that everyone has an intuitive influence on these two methods more intuitively.

  Its depth-first traversal of the result is 1-2-4-8-5-3-6-7
  the result of its breadth-first traversal of1-2-3-4-5-6-7-8

  At this point, the content of our data structure has been introduced, and the next article will introduce the relevant content of the algorithm.

to sum up

  Through the previous articles, we have introduced you to the relevant knowledge of data structure, including the recommendation of data tools , top ten sorting algorithms , overview of data structures and algorithms , linked lists , stacks , queues , sorting and search algorithms, and trees and graphs. Starting from the next article, we will introduce you to the algorithm. In fact, data structures and algorithms are particularly important and play a vital role in programming. Therefore, we need special mastery. Life is endless and struggle is endless. We work hard every day, study hard, constantly improve our abilities, and believe that we will learn something. Come on! ! !

Guess you like

Origin blog.csdn.net/Oliverfly1/article/details/113914315