JavaScript 实现经典数据结构之图

1,263 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

图是由具有边的节点集合组成的数据结构,图可以是定向的或者是不定向的。图具有以下几种基本元素:

  • 节点:Node
  • 边:Edge
  • |V|:图中顶点的总数
  • |E|:图中的连接总数

主要实现一个有向图 Graph 类:

class Graph{
    constructor() {
        // 使用map表述图中顶点关系
        this.AdjList = new Map()
    }
}

先通过创建节点来创建一个图:

let graph = new Graph()
graph.addVertex('A')
graph.addVertex('B')
graph.addVertex('C')
graph.addVertex('D')

添加顶点 addVertex

addVertex(vertex) {
    if(!this.AdjList.has(vertex)) {
        this.AdjList.set(vertex, [])
    } else {
        throw new Error('该顶点已存在!')
    }
}

此时,A、B、C、D顶点都对应一个数组:

'A' -> []
'B' -> []
'C' -> []
'D' -> []

数组将用来存储边,预计得到如下关系:

'A' -> ['B', 'C', 'D']
'B' -> []
'C' -> ['B']
'D' -> ['C']

添加边 addEdge

addEdge(vertex, node) {
    if(this.AdjList.has(vertex)) {
        if(this.AdjList.has(node)) {
            let arr = this.AdjList.get(vertex)
            if(!arr.includes(node)) {
                arr.push(node)
            }
        } else {
            throw new Error('不能添加')
        }
    } else {
        throw new Error(`请先添加顶点${vertex}`)
    }
}

打印图 print

print() {
    // 使用 for of 遍历并打印 this.AdjList
    for (let [key, value] of this.AdjList) {
        console.log(key, value)
    }
}

广度优先遍历 BFS

createVisitedObject() {
    let map = {}
    for (let key of this.AdjList.keys()) {
        arr[key] = false
    }
    return map
}
bfs (initialNode) {
    // 创建一个已访问节点的 map
    let visited = this.createVisitedObject()
    // 模拟一个队列
    let queue = []
    // 第一个节点已访问
    visited[initialNode] = true
    // 第一个节点入队列
    queue.push(initialNode)
    while (queue.length) {
        let current = queue.shift()
        console.log(current)
         // 获得该节点的其他节点关系
        let arr = this.AdjList.get(current)
        for (let elem of arr) {
            // 如果当前节点没有访问过
            if (!visited[elem]) {
                visited[elem] = true
                queue.push(elem)
            }
        }
    }
}

BFS-利用队列实现的搜索算法,实现步骤:

  1. 起始节点作为起始,并初始化一个空对象——visited;
  2. 初始化一个空数组,该数组将模拟一个队列;
  3. 将起始节点标记为已访问;
  4. 将起始节点放入队列中;
  5. 循环直到队列为空。

深度优先 DFS

createVisitedObject() {
    let map = {}
    for (let key of this.AdjList.keys()) {
        arr[key] = false
    }
    return map
}

// 深度优先算法
dfs(initialNode) {
    let visited = this.createVisitedObject()
    this.dfsHelper(initialNode, visited)
}

dfsHelper(node, visited) {
    visited[node] = true
    console.log(node)
    let arr = this.AdjList.get(node)
    // 遍历节点调用 this.dfsHelper
    for (let elem of arr) {
        if (!visited[elem]) {
            this.dfsHelper(elem, visited)
        }
    }
}

DFS-利用递归实现的搜索算法,实现步骤:

  1. 起始节点作为起始,创建访问对象;
  2. 调用辅助函数递归起始节点。

最后说一句

如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下,您的支持是我坚持写作最大的动力,多谢支持。