Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> JavaScript

Complete Graph Class ใน JavaScript


Functions ที่ comment ออกในโค้ดนี้ คุณสามารถสลับมาใช้ได้ เราย้าย Queue, Stack, PriorityQueue classes ไป modules แยกที่ import ได้ด้วย import statements หรือ require calls นี่คือการนำ Graph class ไปใช้งานอย่างสมบูรณ์:

ตัวอย่างโค้ด

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 });
  }
  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);
      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() {
    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);
    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);
        this.edges[nextNode].forEach(edge => {
          edgeQueue.enqueue([nextNode, edge.node], edge.weight);
        });
        explored.add(nextNode);
        s = nextNode;
      }
    }
    return MST;
  }
  kruskalsMST() {
    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]] = {};
      this.edges[this.nodes[i]].forEach(e => (dist[this.nodes[i]][e.node] = e.weight));
      this.nodes.forEach(n => {
        if (dist[this.nodes[i]][n] == undefined) dist[this.nodes[i]][n] = Infinity;
        if (this.nodes[i] === n) dist[this.nodes[i]][n] = 0;
      });
    }
    this.nodes.forEach(i => {
      this.nodes.forEach(j => {
        this.nodes.forEach(k => {
          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;
  }
  connected(a, b) {
    return this.find(a) === this.find(b);
  }
}