在计算机程序设计中,图也是一种非常常见的数据结构,图论其实是一个非常大的话题,在数学上起源于哥尼斯堡七桥问题。
0 - 1
是相邻的,0 - 3
是相邻的。0 - 2
是不相邻的。v1
,v2
...,vn
的一个连续序列, 比如上图中 0 1 5 9
就是一条路径。0 1 5 9
是一条简单路径。0 1 5 6 3 0
。0 - 1
之间有变,那么说明这条边可以保证 0 -> 1
,也可以保证 1 -> 0
。0 -> 1
,不能保证一定可以 1 -> 0
,要根据方向来定。0 - 1
的边,比 4 - 9
的边更远或者用的时间更长。我们知道一个图包含很多顶点,另外包含顶点和顶点之间的连线(边),这两个都是非常重要的图信息,因此都需要在程序中体现出来。
vertexes
用于存储所有的顶点,使用一个数组来保存。adjList
adj 是 adjoin 的缩写,邻接的意思。adjList 用于存储所有的边,这里采用邻接表的形式。class Graph {
constructor() {
this.vertexes = []; // 存储顶点
this.adjList = new Dictionay(); //存储边信息
}
}
[]
,该数组用于存储顶点连接的所有的边.(回顾邻接表的实现方式)// 添加顶点
addVertex(val) {
// 添加点
this.vertexes.push(val)
// 添加点的关系 采用邻接矩阵法 结构用Map
this.adjList.set(val, [])
}
// 添加边
addEdge(val1, val2) {
// 添加边需要传入两个顶点, 因为边是两个顶点之间的边, 边不可能单独存在.
// 这里实现的是无向图, 所以这里不考虑方向问题
this.adjList.get(val1).push(val2)
this.adjList.get(val2).push(val1)
}
toString 方法:为了能够正确的显示图的结果,就是拿出二维数组的每一项。
// 输出图结构
toString() {
let res = ''
for (let i = 0; i < this.vertexes.length; i++) {
res += this.vertexes[i] + "->"
let adj = this.adjList.get(this.vertexes[i])
for (let j = 0; j < adj.length; j++) {
res += adj[j] + ""
}
res += "\n"
}
return res
}
// 测试代码
let graph = new Graph();
// 添加顶点
let myVertexes = ["A", "B", "C", "D", "E", "F", "G", "H", "I"];
for (let i = 0; i < myVertexes.length; i++) {
graph.addVertex(myVertexes[i]);
}
// 添加边
graph.addEdge("A", "B");
graph.addEdge("A", "C");
graph.addEdge("A", "D");
graph.addEdge("C", "D");
graph.addEdge("C", "G");
graph.addEdge("D", "G");
graph.addEdge("D", "H");
graph.addEdge("B", "E");
graph.addEdge("B", "F");
graph.addEdge("E", "I");
和其他数据结构一样,需要通过某种算法来遍历图结构中每一个数据。这样可以保证,在我们需要时,通过这种算法来访问某个顶点的数据以及它对应的边。
// 初始化顶点的颜色
_initializeColor() {
// 白色: 表示该顶点还没有被访问.
// 灰色: 表示该顶点被访问过, 但并未被探索过.
// 黑色: 表示该顶点被访问过且被完全探索过.
let colors = []
for (let i = 0; i < this.vertexes.length; i++) {
colors[this.vertexes[i]] = "white"
}
return colors
}
广度优先搜索算法的思路 广度优先算法会从指定的第一个顶点开始遍历图,先访问其所有的相邻点,就像一次访问图的一层。换句话说,就是先宽后深的访问顶点。
图解 BFS
广度优先搜索的实现
广度优先搜索的代码
// 广度优先搜索
bfs(handle) {
// 1.初始化颜色
let color = this._initializeColor()
// 2. 创建队列
let queue = new Queue
// 3. 将传入的顶点放入队列
queue.enqueue(this.vertexes[0])
// 4.依赖队列操作数据 队列不为空时一直持续
while (!queue.isEmpty()) {
// 4.1 拿到队头
let qVal = queue.dequeue()
// 4.2 拿到队头所关联(相连)的点并设置为访问中状态(灰色)
let qAdj = this.adjList.get(qVal)
color[qVal] = "gray"
// 4.3 将队头关联的点添加到队尾
// 这一步是完成bfs的关键,依赖队列的先进先出的特点。
for (let i = 0; i < qAdj.length; i++) {
let a = qAdj[i]
if (color[a] === "white") {
color[a] = "gray"
queue.enqueue(a)
}
}
// 4.5设置访问完的点为黑色。
color[qVal] = "black"
if (handle) [
handle(qVal)
]
}
}
测试代码
// 调用广度优先算法
let result = "";
graph.bfs(graph.vertexes[0], function (v) {
result += v + " ";
});
console.log(result); // A B C D E F G H I
深度优先搜索的思路:
深度优先搜索算法的实现:
广度优先搜索算法我们使用的是队列,这里可以使用栈完成,也可以使用递归。
方便代码书写,我们还是使用递归(递归本质上就是函数栈的调用)
深度优先搜索算法的代码:
// 深度优先搜索
dfs(handle) {
// 1.初始化颜色
let color = this._initializeColor()
// 2. 遍历所有顶点,开始访问
for (let i = 0; i < this.vertexes.length; i++) {
if (color[this.vertexes[i]] === "white") {
this._dfsVisit(this.vertexes[i], color, handle)
}
}
}
// dfs的递归方法 这里直接使用函数的调用栈
_dfsVisit(val, color, handle) {
// 1. 将颜色设置为访问中
color[val] = "gray"
// 2. 执行相应的回调
if (handle) {
handle(val)
}
// 3. 拿与该点相邻的点,对每个点操作
let adj = this.adjList.get(val)
for (let i = 0; i < adj.length; i++) {
let w = adj[i]
// 如果相邻点未未访问状态,开始访问。
if (color[w] === "white") {
this._dfsVisit(w, color, handle)
}
}
// 4. 处理完后设置为访问过点。
color[val] = "black"
}
测试代码
// 调用深度优先算法
result = "";
graph.dfs(function (v) {
result += v + " ";
});
// 输出深度优先
console.log(result); //A B E I F C D G H
递归的代码较难理解一些,这副图来帮助理解过程:
本专辑所有文章&源码&测试环境均托管在 GitHub 仓库[1] 欢迎同学们 Star 和 Fork。
参考资料
[1]
GitHub 仓库: https://github.com/XPoet/js-data-structures-and-algorithms
专辑:
从 0 开始学习 JavaScript 数据结构与算法(一)前言
从 0 开始学习 JavaScript 数据结构与算法(二)数组结构
从 0 开始学习 JavaScript 数据结构与算法(三)栈
从 0 开始学习 JavaScript 数据结构与算法(四)队列
从 0 开始学习 JavaScript 数据结构与算法(五)优先队列
从 0 开始学习 JavaScript 数据结构与算法(六)单向链表
从 0 开始学习 JavaScript 数据结构与算法(七)双向链表
从 0 开始学习 JavaScript 数据结构与算法(八)集合
从 0 开始学习 JavaScript 数据结构与算法(九)字典
从 0 开始学习 JavaScript 数据结构与算法(十)哈希表
从 0 开始学习 JavaScript 数据结构与算法(十一)树
我是前端鼓励师,致力于帮助前端小伙伴获得认知和能力提升。 关注我,一起学习,一起进步~
作者为小伙伴们精心准备了 Vue / React / Angular / Node.js / 视频教程 / 学习文档 / 面试指南 / 大厂题库 等资料。(关注公众号,发送“学习资料”即可领取)