内容来自《学习JavaScript数据结构与算法(第三版)》,加入了自己的理解和注释,在我的这篇文章中juejin.cn/post/702926… 有应用到这两个算法(数组和对象flat)。
// 图数据结构
class Graph {
constructor(isDirected = false){
this.isDirected = isDirected;
this.vertices = [];
this.adjList = new Map();
}
addVertex(v) {
if(!this.vertices.includes(v)) {
this.vertices.push(v)
this.adjList.set(v,[])
}
}
addEdge(v,w) {
if(!this.adjList.get(v)) {
this.addVertex(v)
}
if(!this.adjList.get(w)) {
this.addVertex(w)
}
this.adjList.get(v).push(w);
if(!this.isDirected) {
this.adjList.get(w).push(v)
}
}
getVertices() {
return this.vertices
}
getAdjList() {
return this.adjList
}
toString() {
let s = '';
for(let i = 0; i < this.vertices.length; i++ ){
s += `${this.vertices[i]} ->`;
const neighbors = this.adjList.get(this.vertices[i]);
for (let j = 0; j<neighbors.length; j++ ){
s += `${neighbors[j]}`;
}
s += '\n'
}
return s
}
}
// 实例化一个图
const graph = new Graph();
const myVertices = ['A','B','C','D','E','F','G','H','I'];
for(let i = 0; i < myVertices.length; i++){
graph.addVertex(myVertices[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')
console.log(graph.toString())
// 图的遍历 (深度优先、广度优先)
/* 概念: 完全探索一个顶点要求我们查看该顶点的每一条边,对于每一条边所连接的没有访问过的顶点,将其标准为未被发现的,并将其加进待访问顶点列表中。
* 广度和深度遍历的待访问列表数据结构不同。 深度是栈结构,广度是队列结构
*/
// 广度(先宽后深)
const Colors = {
WHITE : 0, // 表示该顶点还未被访问过
GREY : 1, // 表示该顶点被访问(发现)过,但未被探索过
BLACK : 2 // 表示该顶点被访问过且被完全探索过
}
// 定义方法initializeColor,将图内所有节点定义为白色,代表未访问过
const initializeColor = vertices => {
const color = {};
for(let i = 0; i < vertices.length; i++){
color[vertices[i]] = Colors.WHITE
}
return color
}
const breadthFirstSearch = (graph,startVertex,callback) => {
const vertices = graph.getVertices();
const adjList = graph.getAdjList();
const color = initializeColor(vertices);
// const queue = new Queue();
const queue = [] // 假装我是队列
queue.push(startVertex);
color[startVertex] = Colors.GREY; // 属于被访问(发现)了
while (queue.length > 0) {
const u = queue.shift();
const neighbors = adjList.get(u);
for(let i = 0; i < neighbors.length; i++){
const w = neighbors[i];
if(color[w] === Colors.WHITE){
color[w] = Colors.GREY;
queue.push(w)
}
}
color[u] = Colors.BLACK;
if(callback) {
callback(u)
}
}
}
const printVertex = (value) => console.log('Visited vertes:' + value)
// breadthFirstSearch(graph,myVertices[0],printVertex)
// 深度 (先深后广) 这里的栈结构并没有被显示的声明出来,这里的栈体现在js的函数调用栈
const depthFirstVisit = (u,color,adjList,callback) => {
color[u] = Colors.GREY;
if(callback){
callback(u);
}
const neighbors = adjList.get(u);
for(let i = 0; i < neighbors.length; i++){
const w = neighbors[i];
if(color[w] === Colors.WHITE){
depthFirstVisit(w,color,adjList,callback)
}
}
color[u] = Colors.BLACK
}
const depthFirstSearch = (graph,callback) => {
const vertices = graph.getVertices();
const adjList = graph.getAdjList();
const color = initializeColor(vertices)
for(let i = 0; i < vertices.length; i++){
if(color[vertices[i]] === Colors.WHITE){
depthFirstVisit(vertices[i],color,adjList,callback)
}
}
}
depthFirstSearch(graph,printVertex)