Article Directory
1. Basic introduction
1) Why is there a picture
- Earlier we learned about linear tables and trees
- Linear tables are limited to a relationship between an immediate predecessor and an immediate successor
- A tree can only have one immediate predecessor, the parent node.
- When we need to represent many-to-many relationships, here we use graphs
2) Give an example
A graph is a data structure in which a node can have zero or more adjacent elements. A connection between two nodes is called a visit. A node may be called a point. As shown in the picture:
3) Common concepts
-
Vertex
-
edge
-
path
-
Undirected graph (below)
-
directed graph
-
weighted graph
4) Representation
There are two ways to represent graphs:
-
Two-dimensional array representation (adjacency matrix)
The adjacency matrix is a matrix that represents the adjacency relationship between vertices in a graph. For a graph with n vertices, the row and col of the matrix represent 1...n
- Linked list representation (adjacency list)
- The adjacency matrix needs to allocate n edge space for each vertex. In fact, many edges do not exist, which will cause a certain loss of space.
- The implementation of the adjacency list only cares about the edges that exist, not the edges that do not exist. So there is no space wasted, the adjacency list consists of array + linked list
illustrate:
- The associated nodes of the node labeled 0 are 1 2 3 4
- The associated node of the node labeled 1 is 0 4
- The nodes associated with the node labeled 2 are 0 4 5
2. Getting Started Case
1) requirements
2) Code implementation
package A10_图;
/*
Date:2022/5/19
author: Blue Friday
describe: //todo
*/
import java.util.ArrayList;
import java.util.Arrays;
public class Graph {
private ArrayList<String> vertexList; // 存储顶点集合
private int[][] edges; // 存储图对应的邻接矩阵
private int numOfEdges; // 表示边的数目
public static void main(String[] args) {
int n = 5;
String VertexValue[] = {
"A", "B", "C", "D", "E"};
Graph graph = new Graph(n);
for (String s : VertexValue) {
graph.insertVertex(s);
}
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.showGraph();
}
// 构造器
public Graph(int n) {
// 初始化 矩阵
edges = new int[n][n];
vertexList = new ArrayList<>(n);
numOfEdges = 0;
}
// 插入结点
public void insertVertex(String vertex) {
vertexList.add(vertex);
}
// 添加边
public void insertEdge(int v1, int v2, int weight) {
// v1:点的下标
// v2:第二个顶点的下标
// weight:表示
edges[v1][v2] = weight;
edges[v2][v1] = weight;
numOfEdges++;
}
// 图常用方法
// 1. 返回节点个数
public int getNumOfVertex() {
return vertexList.size();
}
// 2. 得到边的数目
public int getNumOfEdges() {
return numOfEdges;
}
// 3. 返回节点 i 对应的数据 "0 -> A ; 1 -> B ; 2 -> C"
public String getValueByIndex(int i) {
return vertexList.get(i);
}
// 4. 返回 v1 v2 的权值
public int getWeight(int v1, int v2) {
return edges[v1][v2];
}
// 5. 显示图对应的矩阵
public void showGraph() {
for (int[] link : edges) {
System.out.println(Arrays.toString(link));
}
}
}
3. Depth-first traversal
1) Introduction to graph traversal
The so-called graph traversal is the access to the nodes. There are so many nodes in a graph, how to traverse these nodes requires a specific strategy, generally there are two access strategies
- Depth-first traversal
- breadth first traversal
2) The basic idea of depth-first traversal
Depth First Search of graphs.
-
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 as the initial node, visit its first adjacent node,
It can be understood like this: every time after visiting the current node, first visit the first adjacent node of the current node.
-
We can see that such an access strategy is to dig deeper vertically instead of horizontally accessing all adjacent nodes of a node.
-
Obviously, depth-first search is a recursive process
3) Depth-first traversal algorithm steps
- Visit the initial node v, and mark node v as visited
- Find the first adjacent node w of node v
- If w exists, continue to 4, if not, return to the first step, 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 step 1 2 3 )
- Find the next adjacent node of the w adjacent node of node v, go to step 3
A→B→C
B→D
B→E
4) Code
// ------深度遍历---------------------------------------------------------
// 得到第一个邻接结点的下标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;
}
// 深度优先遍历算法
public void dfs(boolean[] isVisited, int i) {
// 首先,访问该结点
System.out.print(getValueByIndex(i) + " -> ");
// 设置已经访问
isVisited[i] = true;
int w = getFirstNeighbor(i);
while (w != -1) {
// 说明有值
if (!isVisited[w]) {
dfs(isVisited, w);
}
// 如果 w结点被访问过
w = getNextNeighbor(i, w);
}
}
// 重载
public void dfs() {
// 遍历所有结点进行 dfs
for (int i = 0; i < getNumOfEdges(); i++) {
if (!isVisited[i]) {
dfs(isVisited, i);
}
}
}
4. Breadth-first traversal
1 Introduction
Graph Breadth First Search (Broad First Search). Similar to a hierarchical search process, breadth-first traversal needs to use a queue to keep the order of the visited nodes, so as to visit the adjacent nodes of these nodes in this order.
2) Algorithm steps
- Visit the initial node v and mark node v as visited
- Node v enqueues
- When the queue is not empty, continue to execute, otherwise the algorithm ends
- Get out of the queue and take out the queue head node U
- Find the first adjacent node w of node u
- If the adjacent node w of node u does not exist, then go to 3,; otherwise, perform the following three steps in a loop
- If node w has not been visited, visit node w and mark it as visited
- Node w enters the queue
- Find the next adjacent node w after the adjacent node of node u, go to 6
3) Code
// -------广度遍历-----------------------------------------------------------
public void bfs(boolean[] isVisited, int i) {
int u; // 表示队列的头节点对应下标
int w; // 邻接结点w
// 队列,记录结点访问顺序
LinkedList<Integer> queue = new LinkedList<>();
System.out.println(getValueByIndex(i) + " -> ");
// 标记为已访问
isVisited[i] = true;
// 将节点加入队列
queue.addLast(i);
while (!queue.isEmpty()) {
// 取出队列的头结点下标
u = queue.removeFirst();
// 得到第一个邻接结点的下标 w
w = getFirstNeighbor(u);
while (w != -1) {
// 找到
// 是否访问过
if (!isVisited[w]) {
System.out.println(getValueByIndex(w) + " -> ");
// 标记
isVisited[w] = true;
queue.addLast(w);
}
// 以 u 为前驱点,找w后的邻接结点
w = getNextNeighbor(u, w);
}
}
}
public void bfs() {
for (int i = 0; i < getNumOfEdges(); i++) {
if(!isVisited[i]){
bfs(isVisited, i);
}
}
}
5. Compare
int n = 8;
String VertexValue[] = {
"1", "2", "3", "4", "5", "6", "7", "8"};
Graph graph = new Graph(n);
for (String s : VertexValue) {
graph.insertVertex(s);
}
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();
System.out.println("-------dfs-------------");
graph.dfs();
System.out.println();
System.out.println("-------d=bfs-------------");
graph.bfs();
result
[0, 1, 1, 0, 0, 0, 0, 0]
[1, 0, 0, 1, 1, 0, 0, 0]
[1, 0, 0, 0, 0, 1, 1, 0]
[0, 1, 0, 0, 0, 0, 0, 1]
[0, 1, 0, 0, 0, 0, 0, 1]
[0, 0, 1, 0, 0, 0, 1, 0]
[0, 0, 1, 0, 0, 1, 0, 0]
[0, 0, 0, 1, 1, 0, 0, 0]
-------dfs-------------
1 -> 2 -> 4 -> 8 -> 5 -> 3 -> 6 -> 7 ->
-------bfs-------------
1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 ->