算法基础16:图

图的基本介绍

为什么要有图

图的举例说明

图的常用概念

图的表示方式

图的快速入门案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import java.util.ArrayList;
import java.util.Arrays;

/**
* TODO
*
* @author:XMD
* @date: 15.10.22
*/
public class Graph {

//存储顶点集合
private ArrayList<String> vertexList;
//存储图对应的邻结矩阵
private int[][] edges;
//表示边的数目
private int numOfEdges;

public static void main(String[] args) {
//结点个数
int n = 5;
String vertexs[] = {"A", "B", "C", "D", "E"};
//创建图对象
Graph graph = new Graph(n);
//添加顶点
for (String value : vertexs) {
graph.insertVertex(value);
}
//添加边 A-B A-C B-C B-D B-E
graph.insetEdges(0,1,1);
graph.insetEdges(0,2,1);
graph.insetEdges(1,2,1);
graph.insetEdges(1,3,1);
graph.insetEdges(1,4,1);

//显式
graph.showGraph();

}

public Graph(int n) {
edges = new int[n][n];
vertexList = new ArrayList<>(n);
numOfEdges = 0;
}

//显式图
public void showGraph() {
for (int[] edge : edges) {
System.err.println(Arrays.toString(edge));
}

}

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

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

//返回结点对应的数据
public String getValueByIndex(int index) {
return vertexList.get(index);
}

//返回v1和v2的权值
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 insetEdges(int v1, int v2, int weight) {
edges[v1][v2] = weight;
edges[v2][v1] = weight;
numOfEdges++;
}

}

图的深度优先遍历

图的广度优先遍历

广度优先与深度优先遍历的代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;

/**
* TODO
*
* @author:XMD
* @date: 15.10.22
*/
public class Graph {
//存储顶点集合
private ArrayList<String> vertexList;
//存储图对应的邻结矩阵
private int[][] edges;
//表示边的数目
private int numOfEdges;
//记录某个结点是否被访问
private boolean[] isVisited;


public static void main(String[] args) {
String vertexs[] = {"A", "B", "C", "D", "E"};
//创建图对象
Graph graph = new Graph(vertexs.length);
//添加顶点
for (String value : vertexs) {
graph.insertVertex(value);
}
//添加边 A-B A-C B-C B-D B-E
graph.insetEdges(0, 1, 1);
graph.insetEdges(0, 2, 1);
graph.insetEdges(1, 2, 1);
graph.insetEdges(1, 3, 1);
graph.insetEdges(1, 4, 1);

//显式
// graph.showGraph();

// System.out.println("测试DFS");
// graph.dfs();
System.out.println("");
System.out.println("测试BFS");
graph.bfs();
}

public Graph(int n) {
edges = new int[n][n];
vertexList = new ArrayList<>(n);
numOfEdges = 0;
isVisited = new boolean[n];
}

/**
* 得到第一个邻接结点的下标
*
* @param index
* @return 如果存在就返回对应的下标,否则返回-1
*/
public int getFirstNeighbor(int index) {
for (int i = 0; i < vertexList.size(); i++) {
if (edges[index][i] > 0) {
return i;
}
}
return -1;
}

/**
* 根据前一个邻接结点的下标来获取下一个邻接结点
*
* @param v1
* @param v2
* @return
*/
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;
}

/**
* 深度优先遍历算法
*
* @param isVisited
* @param i
*/
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);
}
}
}

/**
* 对一个结点进行广度优先遍历的方法
*
* @param isVisited
* @param i
*/
private void bfs(boolean[] isVisited, int i) {
int u; // 表示队列的头节点对应的下标
int w; //邻接结点W
LinkedList<Object> 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()];
//遍历所有的结点进行bfs
for (int i = 0; i < getNumOfVertex(); i++) {
if (!isVisited[i]) {
bfs(isVisited, i);
}
}
}


//显式图
public void showGraph() {
for (int[] edge : edges) {
System.err.println(Arrays.toString(edge));
}

}

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

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

//返回结点对应的数据
public String getValueByIndex(int index) {
return vertexList.get(index);
}

//返回v1和v2的权值
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 insetEdges(int v1, int v2, int weight) {
edges[v1][v2] = weight;
edges[v2][v1] = weight;
numOfEdges++;
}
}


DFS与BFS 比较

-------------本文结束感谢您的阅读-------------