無厘頭 Graph

前言

今天晚上無心翻到一個圖的文章,查了一下感受網上實現和其餘都好複雜,因此本身按理解搞了一下,不知道是我實現是否是錯了...感受還好~進入正題,先仍是來點理論知識,不過大可能是本身的想法,不必定都對,能夠糾正。內容來源來自《JavaScript 數據結構和算法》。javascript

圖是一種數學模型,和數學掛勾通常都會比較複雜,因此形象的理解成最簡單的模型,點-線 模型。其實最簡單的是 1 個點的模型,涉及 2 個點還好,3 個點事後模型就會做出相應的改變。java

這裏用簡單的語言來講圖中的二元關係,不過仍是先假設一點數學符號:node

G => 表示全部的頂點集合算法

V => 表示頂點數據庫

E => 表示邊,抽象意義上是無向邊數據結構

那麼用數學來表示就是:G=<V, E>數據結構和算法

其實根本不用理解數學的模型,我這裏理解是隻須要知道這是一個點-線模型就能夠了。函數

如何表示圖呢?

這裏有兩種表示方法:表和矩陣,其間都是鄰接關係工具

這裏我有一個測試圖,在網上弄的,雖然是無向圖,其實在咱們代碼中,確定是有向的,是入口的問題:測試

圖

圖的結構肯定事後,就能夠作出表的結構了,這裏我沒有用方向,由於我理解的圖是一個不能簡單表示的,理解成座標系更好理解一點。分爲:x, y 軸的方式。其中,x0 表示開始,後面表示相鄰的點,按順時針排列(不必定按這個順序)。

表

代碼中的圖

在代碼中表示,沒有圖形那麼直觀,因此須要映射成代碼模型,這裏簡單實現一下,可是不具有不少功能。

假設:

class G => 一個圖的類,包括圖的定義和經常使用遍歷方法

this.V => 表示點集合的個數,可是這裏我捨棄了 0 的位置

this.T => 我按數據庫表的方式理解命名的,關係的集合

this.E => 邊的個數

this.visited => 訪問過的 bool 集合,其實就是標記

this.defined => 工具小函數,是否認義過,與圖無關

因此最後有關的符號有:

G、V、T、E

是否是感受一會兒變簡單了,不過程序的抽象有一個上層,那就是類。
而後我這裏按計算機的方式,定義了輸入、輸出函數:input、output

class G {
    constructor(V) {
        this.V = V;
        this.T = [];
        this.E = 0;
        this.visited = [];

        for (let v = 0; v < this.V; ++v) {
            this.T[v] = [];
            this.T[v].push(-1);
        }

        this.defined = s => s !== void 0;
    }
    input(v, w) {
        this.T[v].push(w);
        this.T[w].push(v);

        this.E++;

        return this;
    }
    output() {
        console.table(this.T);
    }
}

而後可以看出,其實邊是由點的鏈接組成的,恰好符合數學的定義,而且與相鄰有相關性。

那麼,實現告終構,還應該有其餘做用,那麼接下來看一下遍歷算法:深度遍歷(DFS) 和 廣度遍歷(BFS)。準確來講應該是優先採用什麼策略的遍歷方式。其實我這裏的實現感受...很差,和樹關係大了點,不過樹的大集合就可以上升到圖。

dfs(v) {
    this.visited[v] = true;

    if (this.defined( this.T[v] )) {
        console.log('老孫到此一遊:' + v);
    }

    this.T[v].forEach(t => {
        if (t !== -1 && !this.visited[t]) {
            this.dfs(t);
        }
    });
}

對於深度遍歷,是將圖按一個固定方向,縱向的結果,因此是一個遞歸的結構。

bfs(node) {
    this.visited[node] = true;

    var queue = [];
    queue.push(node);
    while(queue.length > 0) {
        var v = queue.shift();
        if(this.defined( this.T[v] )) {
            console.log('老孫到此一遊:' + v);
        }

        this.T[v].forEach(t => {
            if(t !== -1 && !this.visited[t]) {
                this.visited[t] = true;
                queue.push(t);
            }
        });
    }
}

對於廣度遍歷,是將圖按一個固定方向,橫向的結果,因此是一個鏈式進出的關係,這裏是用隊列,在 JS 中作隊列這種先進先出比較簡單。

// 測試代碼
var v = [1, 2, 3, 4, 5];
let g = new G( v.length + 1 );
g.input(1, 2).input(1, 5)
    .input(2, 4).input(2, 5).input(2, 3)
    .input(3, 4)
    .input(4, 5)
    .output();
g.dfs(1);
console.log('------------');
// 讓它失憶一下
g.visited = [];
g.bfs(1);

執行結果

……-_-# 簡單玩一下,睡覺了 zZZ

相關文章
相關標籤/搜索