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

โครงสร้างข้อมูล Graph ใน JavaScript


Graph เป็น pictorial representation ของชุด objects ที่บางคู่เชื่อมด้วย links Objects ที่เชื่อมต่อกันแทนด้วย points เรียกว่า vertices และ links ที่เชื่อม vertices เรียกว่า edges

อย่างเป็นทางการ graph เป็นคู่ของ sets (V, E) โดย V คือ set of vertices และ E คือ set of edges เชื่อมคู่ vertices มาดู graph ต่อไปนี้:

โครงสร้างข้อมูล Graph ใน JavaScript

ใน graph ข้างต้น:

V = {a, b, c, d, e}
E = {ab, ac, bd, cd, de}

Terminology

Mathematical graphs แทนได้ใน data structure เราสามารถแทน graph ด้วย array of vertices และ two-dimensional array of edges ก่อนไปต่อ มาทำความรู้จัก terms สำคัญ:

  • Vertex - แต่ละ node ของ graph แทนเป็น vertex labeled circle แทน vertices A ถึง G เป็น vertices สามารถแทนด้วย array ได้ index 0 = A, index 1 = B ฯลฯ
  • Edge - แทน path ระหว่างสอง vertices หรือเส้นเชื่อมสอง vertices ใช้ two-dimensional array แทน AB = 1 at row 0 col 1, BC = 1 at row 1 col 2 ฯลฯ อื่นๆ เป็น 0
  • Adjacency - สอง nodes/vertices adjacent ถ้าเชื่อมผ่าน edge B adjacent กับ A, C adjacent กับ B ฯลฯ
  • Path - ลำดับของ edges ระหว่างสอง vertices ABCD คือ path จาก A ไป D

นี่คือการนำ Graph Class ไปใช้งานอย่างสมบูรณ์ด้วย JavaScript:

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

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);
  }
}