深度优先遍历和广度优先遍历

169 阅读1分钟

内容来自《学习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)