什么是”图”?

图是一种非常重要,且跟现实息息相关的数据结构,它是网络结构的抽象模型。

举个非常常见的例子:

我们每天在使用百度、高德地图进行导航时,城市的地图就是一种图的结构。

我们使用QQ、微信、Twitter、Facebook等社交软件,我们的好友关系网也是一种图的结构。

不仅如此,我们还可以使用图来表示道路、航班以及通信。

图

图的相关术语

一个图由 G = (V,E)组成。

  • V:一组顶点
  • E:一组边,连接V中的顶点

顶点:

图最基本的单元,也就是图中的节点。

边:

顶点之间的关联关系,被称为边。

相邻顶点:

由一条边连接在一起的顶点,被称为相邻顶点。

度:

一个顶点包含的相邻顶点的数量,被称为度。

权重和带权图:

有些图中,每一条边并不是完全等同的。如在地铁线路组成的图中,A站到B站的距离是3km,B站到C站的距离是5km,则该数值便是图的权重,而这种图,则被称为带权图

带权图

有向图:

如果图中节点之间的边线是单向的,则被称为有向图。

有向图

无向图:

如果图中节点之间的边线是双向的,或者没有一个明确的指向,则被称为无向图。

无向图

常用代码实现图的几种方法

邻接矩阵

图最常见的实现是邻接矩阵,也就是使用二维数组,表达各个顶点之间的关联关系。

邻接矩阵

如上图所示,顶点1到顶点2之间有边关联,则矩阵中的元素[0][1]的值为1;顶点3到顶点1没有关联,则矩阵[2][0]的值为0。

图上的邻接矩阵的代码表示:

1
2
3
4
5
6
7
8
9
10
11
let graphMatrix = [
[
0,1,1
],
[
0,0,0
],
[
0,1,0
]
]

邻接表

邻接矩阵虽然能表示图结构,但是占用了太多的空间。而邻接表,是图结构的另外一种表示函数。邻接表由图中每个顶点的相邻顶 点列表所组成,可以使用列表(数组)、链表,甚至是散列表或是字典来表示相邻顶点列表。

邻接表

代码实现

这里以邻接表为例,实现一个图(Graph)。

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
import Dictionary from "../Dictionary/app.js";

export default class Graph {
constructor(isDirected = false) {
this.isDirected = isDirected; // 接收isDirected表示图是否有向,默认情况下图是无向的
this.vertices = []; //初始化一个数组,用于存储图中所有顶点的名字
this.adjList = new Dictionary(); // 创建一个字典实例,用来存储邻接表(字典将会使用顶点的名字作为键-key,邻接顶点列表作为值-value)
}
/**
* 向图中添加一个新顶点
* @param {string} v 需要向图中添加的顶点
*/
addVertex(v) {
if (!this.vertices.includes(v)) {
// 如果这个顶点不存在图中 Array.prototype.includes() 判断数组是否包含特定的元素
this.vertices.push(v); // 则将该顶点添加到顶点列表中
this.adjList.set(v, []); // 并且在邻接表中,设置新插入的顶点(v)作为键(key),对应的字典值(value)为一个空数组
}
}
/**
* 向图中添加两点之间的边
* @param {string} a 传入要添加边的顶点a
* @param {string} b 传入要添加边的顶点b
*/
addEdge(a, b) {
// 先校验两个顶点是否存在图中
if (!this.adjList.get(a)) {
//如果图中不存在顶点a,则将它加入顶点列表
this.addVertex(a);
}
if (!this.adjList.get(b)) {
// 如果图中不存在顶点b,则将它加入顶点列表
this.addVertex(b);
}
this.adjList.get(a).push(b); // 将顶点a加入到顶点b的邻接表(等于添加了一条自顶点a到顶点b的边)
if (this.isDirected !== true) {
// 如果图为无向图
this.adjList.get(b).push(a); // 还要加将顶点b加入到顶点a的领接表
}
}
/**
* 返回图的顶点列表
* @returns {array}
*/
getVertices() {
return this.vertices;
}
/**
* 返回图的邻接表
* @returns {any}
*/
getAdjList() {
return this.adjList;
}
/**
* 将图以字符串的方式输出
* @returns {string}
*/
toString() {
let s = "";
for (let i = 0; i < this.vertices.length; i++) { // 迭代图的顶点列表(vertices)
s += `${this.vertices[i]} -> `; // 将顶点的名字加入字符串中
const neighbors = this.adjList.get(this.vertices[i]); // 接着取得该顶点对应的邻接表
for (let j = 0; j < neighbors.length; j++) { //遍历该邻接表(neighbors)
s += `${neighbors[j]} `; // 将相邻顶点加入我们的字符串
}
s += "\n"; // 邻接表迭代完成后,给我们的字符串添加一个换行符
}
return s; // 输入打印成字符串的图
/**
* A -> B C D
B -> A E F
C -> A D G
D -> A C G H
E -> B I
F -> B
G -> C D
H -> D
I -> E
*/
}
}

后记

笔记中部分图片来自公众号:程序员小灰


https://sothx.com/2021/04/17/Graph/
作者
Sothx
发布于
2021年4月17日
许可协议