用Javascript完成图类

在此代码中已注释掉的功能。您也可以切换到那些。我们还将Queue,Stack和PriorityQueue类移到了可以使用import语句或require调用导入的不同模块中。这是Graph类的完整实现- 

示例

const Queue = require("./Queue");
const Stack = require("./Stack");
const PriorityQueue = require("./PriorityQueue");

class Graph {
   constructor() {
      this.edges = {};
      this.nodes = [];
 }

   addNode(node) {
      this.nodes.push(node);
      this.edges[node] = [];
 }

   addEdge(node1, node2, weight = 1) {
      this.edges[node1].push({ node: node2, weight: weight});
      this.edges[node2].push({ node: node1, weight: weight});
 }

   addDirectedEdge(node1, node2, weight = 1) {
      this.edges[node1].push({ node: node2, weight: weight});
 }

   //addEdge(node1,node2){
      //this.edges [node1] .push(node2); 
      //this.edges [node2] .push(node1); 
   //}

   //addDirectedEdge(node1,node2){
      //this.edges [node1] .push(node2); 
   //}

   display() {
      let graph = "";
      this.nodes.forEach(node => {
         graph += node + "->" + this.edges[node].map(n => n.node).join(", ") + "\n";
    });
      console.log(graph);
 }

   BFS(node) {
      let q = new Queue(this.nodes.length);
      let explored = new Set();
      q.enqueue(node);
      explored.add(node);
      while (!q.isEmpty()) {
         let t = q.dequeue();
         console.log(t);
         this.edges[t].filter(n => !explored.has(n)).forEach(n => {
            explored.add(n);
            q.enqueue(n);
       });
    }
 }

   DFS(node) {
      //创建一个堆栈并在其中添加我们的初始节点
      let s = new Stack(this.nodes.length);
      let explored = new Set();
      s.push(node);

      //将第一个节点标记为已浏览
      explored.add(node);

      //我们将继续直到堆栈变空
      while (!s.isEmpty()) {
         let t = s.pop();

         //记录从堆栈中出来的每个元素
         console.log(t);

         //节点
         //直接连接。
         //2.我们过滤掉已经探索过的节点。
         //3.然后,我们将每个未探索的节点标记为已探索并推动它
         //到堆栈。
         this.edges[t].filter(n => !explored.has(n)).forEach(n => {
            explored.add(n);
            s.push(n);
       });
    }
 }

   topologicalSortHelper(node, explored, s) {
      explored.add(node);
      this.edges[node].forEach(n => {
         if (!explored.has(n)) {
            this.topologicalSortHelper(n, explored, s);
       }
    });
      s.push(node);
 }

   topologicalSort() {
      //创建一个堆栈并在其中添加我们的初始节点
      let s = new Stack(this.nodes.length);
      let explored = new Set();
      this.nodes.forEach(node => {
         if (!explored.has(node)) {
            this.topologicalSortHelper(node, explored, s);
       }
    });
      while (!s.isEmpty()) {
         console.log(s.pop());
    }
 }

   BFSShortestPath(n1, n2) {
      let q = new Queue(this.nodes.length);
      let explored = new Set();
      let distances = { n1: 0};

      q.enqueue(n1);
      explored.add(n1);

      while (!q.isEmpty()) {
         let t = q.dequeue();
         this.edges[t].filter(n => !explored.has(n)).forEach(n => {
            explored.add(n);
            distances[n] = distances[t] == undefined ? 1 : distances[t] + 1;
            q.enqueue(n);
       });
    }
      return distances[n2];
 }

   primsMST() {
      //初始化将包含MST的图形
      const MST = new Graph();
      if (this.nodes.length === 0) {
         return MST;
    }

      //选择第一个节点作为起始节点
      let s = this.nodes[0];

      //创建一个优先级队列并浏览集
      let edgeQueue = new PriorityQueue(this.nodes.length * this.nodes.length);
      let explored = new Set();

      explored.add(s);
      MST.addNode(s);

      //将所有从此起始节点开始的边以权重作为优先级添加到PQ-
      this.edges[s].forEach(edge => {
         edgeQueue.enqueue([s, edge.node], edge.weight);
    });

      //选取最小的边缘并将其添加到新图形中
      let currentMinEdge = edgeQueue.dequeue();
      while (!edgeQueue.isEmpty()) {
         //继续去除边缘,直到我们得到带有未开发节点的边缘
         while (!edgeQueue.isEmpty() && explored.has(currentMinEdge.data[1])) {
            currentMinEdge = edgeQueue.dequeue();
       }
         let nextNode = currentMinEdge.data[1];

         //再次检查,因为队列可能会变空而没有退还未开发的元素
         if (!explored.has(nextNode)) {
            MST.addNode(nextNode);
            MST.addEdge(currentMinEdge.data[0], nextNode, currentMinEdge.priority);
            //再次将所有边添加到PQ-
            this.edges[nextNode].forEach(edge => {
               edgeQueue.enqueue([nextNode, edge.node], edge.weight);
          });

            //将此节点标记为exploredexplored.add(nextNode); 
            s = nextNode;
       }
    }
      return MST;
 }

   kruskalsMST() {
      //初始化将包含MST的图形
      const MST = new Graph();
      this.nodes.forEach(node => MST.addNode(node));
      if (this.nodes.length === 0) {
         return MST;
    }

      //创建一个优先级队列
      let edgeQueue = new PriorityQueue(this.nodes.length * this.nodes.length);

      //将所有边添加到队列中:
      for (let node in this.edges) {
         this.edges[node].forEach(edge => {
            edgeQueue.enqueue([node, edge.node], edge.weight);
       });
    }

      let uf = new UnionFind(this.nodes);
      //循环直到我们探索所有节点或队列为空
      while (!edgeQueue.isEmpty()) {
         //使用解构获取边缘数据
         let nextEdge = edgeQueue.dequeue();
         let nodes = nextEdge.data;
         let weight = nextEdge.priority;
         if (!uf.connected(nodes[0], nodes[1])) {
            MST.addEdge(nodes[0], nodes[1], weight);
            uf.union(nodes[0], nodes[1]);
       }
    }
      return MST;
 }

   djikstraAlgorithm(startNode) {
      let distances = {};
      //存储对先前节点的引用
      let prev = {};
      let pq = new PriorityQueue(this.nodes.length * this.nodes.length);

      //以外的所有节点的距离设置为无限
      distances[startNode] = 0;
      pq.enqueue(startNode, 0);
      this.nodes.forEach(node => {
         if (node !== startNode) distances[node] = Infinity;
         prev[node] = null;
    });

      while (!pq.isEmpty()) {
         let minNode = pq.dequeue();
         let currNode = minNode.data;
         let weight = minNode.priority;

         this.edges[currNode].forEach(neighbor => {
            let alt = distances[currNode] + neighbor.weight;
            if (alt < distances[neighbor.node]) {
               distances[neighbor.node] = alt;
               prev[neighbor.node] = currNode;
               pq.enqueue(neighbor.node, distances[neighbor.node]);
          }
       });
    }
      return distances;
 }

   floydWarshallAlgorithm() {
      let dist = {};
      for (let i = 0; i < this.nodes.length; i++) {
         dist[this.nodes[i]] = {};
         //对于现有的边缘,将dist设置为与weight相同
         this.edges[this.nodes[i]].forEach(
            e => (dist[this.nodes[i]][e.node] = e.weight)
         );

         this.nodes.forEach(n => {
            //对于所有其他节点,将其分配给infinity-
            if (dist[this.nodes[i]][n] == undefined)
            dist[this.nodes[i]][n] = Infinity;
            //对于自身边缘,将dist分配为0-
            if (this.nodes[i] === n) dist[this.nodes[i]][n] = 0;
       });
    }

      this.nodes.forEach(i => {
         this.nodes.forEach(j => {
            this.nodes.forEach(k => {
               //检查从i到k再从k到j是否更好
               //而不是直接从i转到j。如果是,则更新
               //我将j值更改为新值
               if (dist[i][k] + dist[k][j] < dist[i][j])
                  dist[i][j] = dist[i][k] + dist[k][j];
          });
       });
    });
      return dist;
 }
}

class UnionFind {
   constructor(elements) {
      //断开连接的组件数量
      this.count = elements.length;

      //跟踪连接的组件
      this.parent = {};

      //初始化数据结构,使所有
      //元素有自己的父母
      elements.forEach(e => (this.parent[e] = e));
 }

   union(a, b) {
      let rootA = this.find(a);
      let rootB = this.find(b);

      //根是相同的,因此它们已经连接。
      if (rootA === rootB) return;

      //始终将根较小的元素作为父元素。
      if (rootA < rootB) {
         if (this.parent[b] != b) this.union(this.parent[b], a);
         this.parent[b] = this.parent[a];
    } else {
         if (this.parent[a] != a) this.union(this.parent[a], b);
         this.parent[a] = this.parent[b];
    }
 }

   //返回节点的最终父级
   find(a) {
      while (this.parent[a] !== a) {
         a = this.parent[a];
    }
      return a;
 }

   //检查2个节点的连接
   connected(a, b) {
      return this.find(a) === this.find(b);
 }
}