圖的JS實現

圖的定義

圖就是由若干個頂點和邊鏈接起來的一種結構。不少東西均可以用圖來講明,例如人際關係,或者地圖。數據結構

其中圖還分爲有向圖和無向圖。
以下就是有向圖函數

圖的數據結構

對於圖這種關係,能夠經過兩種方式來存儲。this

領接表

將每一個頂點與其相鄰的頂點存儲起來。code

鄰接矩陣

將頂點間的相鄰關係用0和1來表示,0表示不相鄰,1表示相鄰。隊列

圖的實現

以下采用鄰接表結構實現。get

構造函數

class Graph {
  constructor() {
    this.vertices = [];
    this.adjList = new Map();
  }
}

verrices存儲全部的頂點,adjList存儲頂點間的關係。it

addVertex(v) {
  this.vertices.push(v);{1}
  this.adjList.set(v, []);{2}
}
addEdge(v, w) {
  this.adjList.get(v).push(w);
  this.adjList.get(w).push(v);
}

addVertex函數爲新增頂點。{2}是爲新增的頂點初始化一個爲空的鄰接點。
addEdge函數爲關聯兩個頂點爲鄰接點。因爲本次示例的是無向圖,所以每一個頂點都互相增長爲鄰接點。class

遍歷

圖的遍歷分爲廣度優先遍歷和深度優先遍歷。
廣度優先遍歷就是從一個頂點開始,一層一層的遍歷頂點。而深度優先遍歷,是從一個頂點開始,選擇一個路徑一直深刻遍歷,直到到達該路徑的盡頭,再返回去,按照一樣的方式,遍歷其餘頂點。
不管廣度優先仍是深度優先,大體原理都是,建立一個字段,從一個頂點開始,將其鄰接點一個個存入該字段中,惟一的區別是,廣度優先遍歷,該字段用隊列存儲,而深度優先遍歷用棧存儲。
然而因爲本次示例的圖爲無向圖,會出現重複遍歷的狀況,例如A頂點的鄰接點有B,B的鄰接點有A。所以須要建立一個類,來維護這些頂點哪些已經被遍歷過了,哪些尚未遍歷。原理

class GraphStatus {
  constructor() {
    this.status = {};
  }
  detect(key) {
    this.status[key] = true;
  }
  isDetected(key) {
    return !!this.status[key];
  }
}

以下是廣度優先遍歷List

bfs(v, cb) {
   const queue = [],
    graphStatus = new GraphStatus();
  queue.push(v);
  while (queue.length > 0) {
     const u = queue.shift();
    graphStatus.detect(u);
    this.adjList.get(u).forEach(item => {
      if (!graphStatus.isDetected(item)) {
        graphStatus.detect(item);
        queue.push(item);
      }
    });
    if (cb) {
      cb(u);
    }
  }
}

以下是深度優先遍歷

dfs(v, cb) {
   const stack = [],
    colorStatus = new GraphStatus();
  stack.push(v);
  while (stack.length > 0) {
    const u = stack.pop();
    colorStatus.detect(u);
    this.adjList.get(u).forEach(item => {
      if (!colorStatus.isDetected(item)) {
        colorStatus.detect(item);
        stack.push(item);
      }
    });
    cb(u);
  }
}

最短路徑

基於廣度優先遍歷,能夠很輕易的算出最短路徑。

findDepth(v) {
   let queue = [],
    colorStatus = new GraphStatus(),
    vPath = { [v]: [v] },
    vDepth = {},
    depth = 0;
  queue.push(v);
  while (queue.length > 0) {
    depth++;
    const u = queue.shift();
    colorStatus.detect(u);
    // 相鄰頂點
    const edgeVertex = this.adjList.get(u);
    edgeVertex.forEach(item => {
      if (!colorStatus.isDetected(item)) {
        // 深度統計
        vDepth[item] = depth;
        // 路徑統計
        vPath[item] = [...vPath[u], item];
        colorStatus.detect(item);
        queue.push(item);
      }
    });
  }
  return { depth: vDepth, path: vPath };
}
相關文章
相關標籤/搜索