Java data structure and algorithm 4-graph

1. Figure

1 Introduction

1 Overview

When we need to express a many-to-many relationship, here we use graphs.

A graph is a data structure data structure in which nodes can have zero or more adjacent elements. The connection between two nodes is called an edge. Nodes can also be called points.

2. Basic concepts of graphs

顶点(vertex)
边(edge)

Undirected graph: The connection between vertices has no direction, such as AB, which can be A->B or B->A.

Path: For example, the path from D -> C is

D->B->C或者D->A->B->C。

image-20200930154929108

Directed graph: The connection between vertices has a direction, such as AB, which can only be A->B, not B->A.

Weighted graph: This kind of sideband weight graph is also called net.

image-20200930155048430

3. The basic representation of the graph

There are two ways to represent the graph: two-dimensional array representation (adjacency matrix); linked list representation (adjacent list).

(1) Adjacency matrix

The adjacency matrix is ​​a matrix that represents the adjacent relationship between vertices in a graph. For a graph with n vertices, the row and col of the matrix represent 1...n points.

image-20200930155130164

(2) Adjacency list

The adjacency matrix needs to allocate the space of n edges for each vertex. In fact, there are many edges that do not exist, which will cause a certain loss of space.

The realization of the adjacency list only cares about the existing edges, not the non-existent edges. Therefore, there is no space waste, and the adjacency list is composed of an array + a linked list.

image-20200930155209804

1、标号为0的结点的相关联的结点为 1 2 3 4
2、标号为1的结点的相关联结点为0 4,
3、标号为2的结点相关联的结点为 0 4 5

2. Depth First Search

1 Introduction

(1 Overview

The so-called traversal of the graph 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: (1) Depth-first traversal (2) Breadth-first traversal.

(2) Basic idea

(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 the visited adjacent node Point as the initial node, visit its first adjacent node, it can be understood as follows: every time after visiting the current node, first visit the first adjacent node of the current node.

(2) We can see that such an access strategy prioritizes digging vertically, rather than horizontally accessing all adjacent nodes of a node. Obviously, depth-first search is a recursive process

(3) Algorithm steps

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

(4) Demand

Perform a depth-first traversal on the image below, starting from A.

image-20200930155518345

2. Code

package com.xiaolun.graph;

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) {
    
    
      String Vertexs[] = {
    
    "A", "B", "C", "D", "E"};

      Graph graph = new Graph(5);
      for (String vertex : Vertexs) {
    
    
         graph.insertVertex(vertex);
      }

      //A-B A-C B-C B-D B-E
      graph.insertEdge(0, 1, 1); // A-B
      graph.insertEdge(0, 2, 1);
      graph.insertEdge(1, 2, 1);
      graph.insertEdge(1, 3, 1);
      graph.insertEdge(1, 4, 1);
      
      graph.showGraph();
      System.out.println("深度优先遍历----------");
      graph.dfs(); // A->B->C->D->E
   }

   //构造器
   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;
   }


   /**
    * 根据v1的前一个邻接节点的下标来获取下一个邻接节点
    * 此时,v2仅仅代表被遍历过的一个数据,我们在其基础上进行遍历
    * @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         需要访问节点 i(第一次时,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); //递归
         }
         /**
          * 1、如果 w 结点已经被访问过的情况
          * 2、i表示正在被访问的节点,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);
         }
      }
   }

   //图中常用方法
   //返回节点个数
   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的权值(默认为1)
   public int getWeight(int v1, int v2) {
    
    
      return edges[v1][v2];
   }

   //插入节点
   public void insertVertex(String vertex) {
    
    
      vertexList.add(vertex);
   }

   /**
    * 添加边(无向图)
    *
    * @param v1     表示点的下标,即使第几个顶点"A"-"B" "A"(第1个顶点)->0 "B"->1
    * @param v2     第二个顶点对应的下标,如果表示二者有关系v1,v2传入 0,1即可。
    * @param weight (0/1)矩阵里面想用什么来表示他们之间是关联的。
    */
   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/weixin_43334389/article/details/114114285