0
点赞
收藏
分享

微信扫一扫

JavaScript数据结构——图


文章目录

  • ​​图的术语​​
  • ​​图的三种表示​​
  • ​​创建Graph类​​
  • ​​图的遍历​​
  • ​​广度优先搜索​​
  • ​​使用BFS寻找最短路径​​
  • ​​深度优先搜索​​

图的术语

图是网络结构的抽模型。是一组由边连接的节点,在二元关系中是使用图结构来表示的。

相邻顶点:通过一条边连接在一起的顶点

图的三种表示

  1. 邻接矩阵,每一个节点都和一个整数关联,该整数作为节点的索引。
  2. 邻接表,由图中每一个顶点的相邻顶点列表组成
  3. 关联矩阵,矩阵的行表示顶点,列表示边

创建Graph类

字典Dictionary的定义,在前面的文章中已经声明了,这里直接引入​​字典​​

class Graph {
constructor(isDirected = false) {
this.isDirected = isDirected; // 图是否有向
this.vertice = []; // 图顶点的名字
this.adjList = new Dictionary(); // 用字典来存储邻接表
}
/**
*
* @param {*} value
* 添加顶点value
* 如果这个顶点没有存在于图中,那就将这个顶点加入顶点列表中。
*/
addVertex(value) {
if (!this.vertice.includes(value)) {
this.vertice.push(value);
this.adjList.set(value, [])
}
}
/**
* 连接顶点
* @param {*顶点} value
* @param {*顶点} w
*
* 这个函数接受两个顶点,
* 先判断两个顶点是否存在于图中,如果没有,就先将他们加入顶点列表中
*/
addEdge(value,) {
if (!this.adjList.get(value)) {
this.addVertex(value);
}

if (!this.adjList.get(w)) {
this.addVertex(w);
}
this.adjList.get(value)["value"].push(w);
if (!this.isDirected) {
this.adjList.get(w)["value"].push(value)
}
}

getVertices() {
return this.vertice;
}

getAdjList() {
return this.adjList;
}

toString() {
let s = '';
for (let index = 0; index < this.vertice.length; index++) {
s += `${this.vertice[index]}->`;
const neighbors = this.adjList.get(this.vertice[index])['value'];
neighbors.forEach(item => {
s += `${item}`
})
s += '\n';
}

return s;
}
}

图的遍历

图的遍历,必须追踪每个第一次访问的顶点,并且追踪有哪些顶点还没有被完全探索。

完全探索顶点,需要我们查看该顶点的每一条边,如果一条边的所有连接都没有被访问,那么这个顶点就是未被访问。

有两种方式:

  1. 广度优先搜索,
  2. 深度优先搜索

使用这两种方式来遍历图,我们需要对顶点进行标记【白色:顶点没有访问;灰色:表示该顶点被访问过,但并未被探索过;黑色:表示该顶点被访问过,但并未被探索过。】

const Colors = {
WHITE: 0, // 顶点还没有被访问
GREY: 1, // 顶点被访问过,但是没有被探索过
BLACK: 2, // 顶点被访问且被探索过
}

另外还需要一个辅助方法来存储顶点是否被访问过。默认所有顶点都被标记为白色。

// 广度优先和深度优先的算法,需要一个辅助方法来存储顶点是否被访问过
// 在每个算法的开头,所有顶点都标记为白色,未被访问
const initializeColor = vertice => {
const color = {};
for (let index = 0; index < vertice.length; index++) {
color[vertice[index]] = Colors.WHITE;
}
return color;
}

广度优先搜索

从第一个顶点开始访问,先访问所有的相邻顶点。

1、首先是用initializeColor 函数给所有顶点初始化为白色;
2、创建一个队列Q,用来存储待访问和待探索的顶点;
3、breadthFirstSearch函数是接收一个图、起始顶点和一个回调函数;
4、如果队列Q非空,那么我们可以操作队列Q,从队列中出一个顶点,并且获得该顶点所有邻点的邻接表,给该顶点设置为灰色;遍历该顶点的所有邻点。

const breadthFirstSearch = (graph, startVertex,) => {
const vertice = graph.getVertices();
const adjList = graph.getAdjList();
const color = initializeColor(vertice);
const queue = new Queue();
queue.enqueue(startVertex);

while (!queue.isEmpty()) {
// 从队列中获取一个顶点
const u = queue.dequeue();
// 获取该顶点的所有邻点的邻接表
const neighbors = adjList.get(u)['value'];
// 该顶点被初始化为灰色
color[u] = Colors.GREY;

// 遍历顶点的每一个邻点
for (let index = 0; index < neighbors.length; index++) {
// 获取该邻点的值【该顶点的名字】
const w = neighbors[index];
// 如果该邻点未被访问【白色】,
if (color[w] === Colors.WHITE) {
// 标记为灰色
color[w] = Colors.GREY;
// 入栈,当这个顶点出栈的时候,我们可以完成对该顶点的探索。
queue.enqueue(w);
}
}

color[u] = Colors.BLACK;
if (callback) {
callback(u)
}
}
}

队列的声明:​​Queue​​

使用BFS寻找最短路径

给出一个图G和顶点v,找出每一个顶点u和v的之间的最短距离:

// 寻找最短距离
const BFS = (graph,) => {
const vertice = graph.getVertices();
const adjList = graph.getAdjList();

const color = initializeColor(vertice);
const queue = new Queue();

const distances = {}; // 距离
const predecessors = {}; // 前溯点
queue.enqueue(startVertex);

for (let index = 0; index < vertice.length; index++) {
distances[vertice[index]] = 0;
predecessors[vertice[index]] = null;
}

while (!queue.isEmpty()) {
const u = queue.dequeue();
const neighbors = adjList.get(u)['value'];
color[u] = Colors.GREY;

for (let index = 0; index < neighbors.length; index++) {
const w = neighbors[index];
if (color[w] === Colors.WHITE) {
color[w] = Colors.GREY;
distances[w] = distances[u] + 1;

predecessors[w] = u;
queue.enqueue(w);
}
}
color[u] = Colors.BLACK;
}
return {
distances,
predecessors
};
}

执行:

const graph = new Graph();
const arr = ["A", "B", "C", "D", "E", "F", "G", "H", "I"]

arr.forEach(item => {
graph.addVertex(item)
})

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');

console.log("最短距离",BFS(graph,"B"))

在图graph中个各个顶点到点B的距离,如下图:

JavaScript数据结构——图_算法


B到B的距离为0,B到A的距离为1。

深度优先搜索

先深度,再广度:

// 深度优先搜索
const depthFirstSearch = (graph,) => {
const vertice = graph.getVertices();
const adjList = graph.getAdjList();
const color = initializeColor(vertice);

for (let index = 0; index < vertice.length; index++) {
if (color[vertice[index]] === Colors.WHITE) {
depthFirstSearchVisit(vertice[index], color, adjList, callback)
}
}
}

const depthFirstSearchVisit = (u, color, adjList,) => {
color[u] = Colors.GREY;
if (callback) {
callback(u);
}

const neighbors = adjList.get(u)['value'];
for (let index = 0; index < neighbors.length; index++) {
const w = neighbors[index];
if (color[w] === Colors.WHITE) {
depthFirstSearchVisit(w, color, adjList, callback)
}
}

color[u] = Colors.BLACK;
}

分析如图:

JavaScript数据结构——图_深度优先搜索_02

持续更新中


举报

相关推荐

0 条评论