必看干货:如何在 JavaScript 中实现 8 种基本图形算法

在本文中,我将实现8 种图算法,探索 JavaScript 中图的搜索和组合问题(图遍历、最短路径和匹配)。

这些问题是从《Java编程面试要素》一书中借来的。本书中的解决方案是用 Java、Python 或 C++ 编写的,具体取决于书的版本。

尽管问题建模背后的逻辑与语言无关,但我在本文中提供的代码片段使用了一些 JavaScript 警告。

每个问题的每个解决方案都分为 3 个部分:解决方案概述、伪代码,最后是 JavaScript 中的实际代码。

要测试代码并查看它执行预期的操作,您可以使用Chrome 的开发工具在浏览器本身上运行代码段,或使用 NodeJS 从命令行运行它们。

图实现

图的两种最常用的表示是邻接表和邻接矩阵。

我要解决的问题是稀疏图(很少的边),邻接表方法中的顶点操作采用常数(添加一个顶点,O(1))和线性时间(删除一个顶点,O(V+E) ))。所以我会在很大程度上坚持这个实现。

让我们使用adjacency list用一个简单的无向、未加权的图实现来解决这个问题。我们将维护一个对象 (adjacencyList),该对象将包含我们图中的所有顶点作为键。这些值将是所有相邻顶点的数组。在下面的示例中,顶点 1 连接到顶点 2 和 4,因此 adjacencyList: { 1 : [ 2, 4 ] } 等其他顶点。

为了构建图形,我们有两个函数:addVertexaddEdge。addVertex 用于向列表中添加一个顶点。addEdge 用于通过将相邻顶点添加到源和目标数组来连接顶点,因为这是一个无向图。要制作有向图,我们可以简单地删除下面代码中的第 14-16 和 18 行。

在移除一个顶点之前,我们需要遍历相邻顶点的数组并移除所有可能与该顶点的连接。

使用邻接表实现的无向无权图

class Graph {
  constructor() {
    this.adjacencyList = {};
  }
  addVertex(vertex) {
    if (!this.adjacencyList[vertex]) {
      this.adjacencyList[vertex] = [];
    }
  }
  addEdge(source, destination) {
    if (!this.adjacencyList[source]) {
      this.addVertex(source);
    }
    if (!this.adjacencyList[destination]) {
      this.addVertex(destination);
    }
    this.adjacencyList[source].push(destination);
    this.adjacencyList[destination].push(source);
  }
  removeEdge(source, destination) {
    this.adjacencyList[source] = this.adjacencyList[source].filter(vertex => vertex !== destination);
    this.adjacencyList[destination] = this.adjacencyList[destination].filter(vertex => vertex !== source);
  }
  removeVertex(vertex) {
    while (this.adjacencyList[vertex]) {
      const adjacentVertex = this.adjacencyList[vertex].pop();
      this.removeEdge(vertex, adjacentVertex);
    }
    delete this.adjacencyList[vertex];
  }  
}

图遍历

在上一节中实现图的基础上,我们将实现图遍历:广度优先搜索和深度优先搜索。

广度优先搜索

BFS 一次访问一层节点。为了防止多次访问同一个节点,我们将维护一个访问过的对象。

由于我们需要以先进先出的方式处理节点,因此队列是要使用的数据结构的一个很好的竞争者。时间复杂度为 O(V+E)。

function BFS
   Initialize an empty queue, empty 'result' array & a 'visited' map
   Add the starting vertex to the queue & visited map
   While Queue is not empty:
     - Dequeue and store current vertex
     - Push current vertex to result array
     - Iterate through current vertex's adjacency list:
       - For each adjacent vertex, if vertex is unvisited:
         - Add vertex to visited map
         - Enqueue vertex
   Return result array

深度优先搜索

DFS 深度访问节点。由于我们需要以后进先出的方式处理节点,因此我们将使用堆栈

从一个顶点开始,我们将相邻的顶点推送到我们的堆栈中。每当一个顶点被弹出时,它就会在我们的访问对象中被标记为已访问。它的相邻顶点被推入堆栈。由于我们总是弹出一个新的相邻顶点,我们的算法将始终探索一个新的级别

我们还可以使用内部堆栈调用来递归地实现 DFS。逻辑是一样的。

时间复杂度和BFS一样,O(V+E)。

function DFS
   Initialize an empty stack, empty 'result' array & a 'visited' map
   Add the starting vertex to the stack & visited map
   While Stack is not empty:
     - Pop and store current vertex
     - Push current vertex to result array
     - Iterate through current vertex's adjacency list:
       - For each adjacent vertex, if vertex is unvisited:
         - Add vertex to visited map
         - Push vertex to stack
   Return result array

Graph.prototype.bfs = function(start) {
    const queue = [start];
    const result = [];
    const visited = {};
    visited[start] = true;
    let currentVertex;
    while (queue.length) {
      currentVertex = queue.shift();
      result.push(currentVertex);
      this.adjacencyList[currentVertex].forEach(neighbor => {
        if (!visited[neighbor]) {
          visited[neighbor] = true;
          queue.push(neighbor);
        }
      });
    }
    return result;
}
Graph.prototype.dfsRecursive = function(start) {
    const result = [];
    const visited = {};
    const adjacencyList = this.adjacencyList;
    (function dfs(vertex){
      if (!vertex) return null;
      visited[vertex] = true;
      result.push(vertex);
      adjacencyList[vertex].forEach(neighbor => {
          if (!visited[neighbor]) {
            return dfs(neighbor);
          }
      })
    })(start);
    return result;
}
Graph.prototype.dfsIterative = 
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值