1. 程式人生 > >用js來實現那些數據結構15(圖01)

用js來實現那些數據結構15(圖01)

ice ring repeat fan lB tool ati ges lba

其實在上一篇介紹樹結構的時候,已經有了一些算法的相關內容介入。而在圖這種數據結構下,會有更多有關圖的算法,比如廣度優先搜索,深度優先搜索最短路徑算法等等。這是我們要介紹的最後一個數據結構。同時也是本系列最為復雜的一個。那麽我們先來簡單介紹一下,什麽是圖?

一、圖的概念

  簡單說,圖就是網絡結構的抽象模型,圖是一組由連接的節點(或頂點)。任何二元關系都可以用圖來表示。比如我們的地圖,地鐵線路圖等。都是圖的實際應用。

  接著我們看看圖的一些相關概念和術語。

  一個圖G = (V,E)由以下元素組成:

    V:一組頂點。

    E:一組邊,鏈接V中的頂點。

  在繼續之前我們先來上張圖,繼續我們的看圖說話。

技術分享圖片

  請看上圖,我們來解釋一些概念。

    1、由一條邊連接在一起的頂點稱為相鄰頂點。比如上圖中的A和B,A和C,A和D都是相鄰的,但是A和E不是相鄰的。

    2、一個頂點的取決於其相鄰頂點的數量。也就是說,有多少個頂點與其相連,那麽它的度就是多少。比如A的度是3,D的度就是4。

    3、路徑是頂點V1,V2.....Vn的一個連續序列,其中Vi和Vi+1是相鄰的。比如上圖中的ACDG,ABEI都是一個路徑。

    4、簡單路徑要求不包含重復的定點。比如ADG就是一條簡單路徑。

    5、除去最後一個頂點(因為它和第一個頂點時相同的),也是一個簡單路徑,比如ADCA。

    6、如果圖中不存在環。則該圖是無環

的。

    7、如果圖中每兩個頂點間都存在路徑,則該圖是連通的。

  為了便於對比,我又花了一張圖。

技術分享圖片

  跟第一幅圖幾乎是一樣的,只不過我們在路徑上加了點東西。

    8、圖可以是有向的(邊有方向)或者是無向的(邊沒有方向)。比如上圖我們在邊上加了方向就變成了有向圖。

    9、如果在圖中的兩個頂點間在雙向上都存在路徑,則該圖是強連通的。比如上圖中我們可以說C和D是強連通的。A和B不是強連通的。但是上圖並不是一個強連通圖。因為上圖並不是兩個點都有雙向的路徑。

    10、圖還可以是未加權的或是加權的。上圖邊上加的數字就是加權值。(加權的意思可以簡單理解為CSS選擇器中的那種權重。)

二、圖的表示方法

  我們可以表示圖的方法有很多。根據我們要解決問題的類型和圖的類型。我們可以選擇不同的方法來表示圖。下面我們會簡單介紹兩種表示圖的方法。

  1、鄰接矩陣。每一個節點都和一個整數相關聯,該整數將作為數組的索引。我們用一個二維數組來表示各個頂點之間的連接情況。比如索引為i的節點和索引為j的節點相鄰,則表示為arrya[i][j]=1。否則arrya[i][j]=0。

  技術分享圖片

  鄰接矩陣看起來就是這樣子的。要註意我們上面的鄰接矩陣只是表示兩個頂點是否相鄰。我們還需要一個數組來存儲所有的頂點。

  但是鄰接矩陣會有一些性能問題。比如我們會用很多的空間來表示一些根本就不存在的邊。比如上圖所有的0。再比如我們想要找到A頂點的相鄰頂點,即使A頂點只有一個相鄰頂點。我們也必須遍歷整個數組才能找到。

  2、鄰接表,鑒於以上的問題。我們在本篇中所使用的圖的表示方法就是鄰接表。鄰接表由圖中每個頂點的相鄰頂點列表所組成。我們可以用數組,鏈表,map或者hashMap來實現鄰接表。

技術分享圖片

  鄰接表看起來就像是上圖這樣。

  那麽我們知道了圖的一些基本概念和我們要使用的圖的表示方法。下面我們先來完成我們Graph類的架子。

技術分享圖片
function Map () {
    //......其他各種方法,詳見前面的字典部分
}

//代碼很簡單,但是還是要解釋一下。
function Graph() {
    //vertices數組存放我們圖中所有的頂點
    var vertices = [];
    //adjList存放我們的鄰接表。adjList會使用頂點來作為鍵,鄰接頂點列表作為值
    var adjList = new Map();
    //添加頂點的方法。
    this.addVertices = function (v) {
        //存放到頂點數組中
        vertices.push(v);
        //生成一個還沒有鄰接頂點列表的Map,因為這時我們已經有頂點了,所以要生成以待使用
        adjList.set(v,[]);
    }
    //這裏有個小細節我們需要註意,哦對,這是為圖添加邊的方法。要註意的是,實際上,在代碼中,我們是沒有一個東西(變量或者其他什麽)來代表邊的。
    //我們為兩個頂點之間添加一個邊實際上只是為兩個頂點的鄰接表中加入彼此。這樣就代表了這兩個頂點是相鄰的。
    this.addEdge = function (v,w) {
        //而這裏我們所實現的圖是無向圖,所以需要給兩個頂點所對應的鄰接表加入彼此。
        //而如果是有向圖的話,只需要根據方向添加一個就可以了。
        adjList.get(v).push(w);
        adjList.get(w).push(v);
    }

    // 為了方便觀察,我們再實現一個toString方法
    // 沒啥好說的,雙重循環遍歷兩個數組。
    this.toString = function () {
        var s = "";

        for(var i = 0;i < www.baohuayule.net www.taohuayuan178.com vertices.length;i++) {
            s += vertices[i] + "->";
            var neighbors = adjList.get(vertices[i]);
            for(var j = 0; www.120xh.cn  j www.baohuayule.com < neighbors.length; j++) {
                s += neighbors[jwww.feifanyule.cn] + ‘  ‘;
            }
            s += ‘\n‘;
        }
        return s;
    }
}

//我們來試一下
var graph = new Graph(www.255055.cn/);

var verticesArray = [‘A‘,‘B‘,‘C‘,‘D‘,‘E‘,‘F‘,‘G‘,‘H‘,‘I‘];

for(var i = 0; i < verticesArray.length; i++) {
    graph.addVertices(verticesArray[i]);
}

graph.addEdge(‘A‘,‘B‘);
graph.addEdge(‘A‘,‘C‘);
graph.addEdge(‘A‘,‘D‘);
graph.addEdge(‘C‘,‘D‘);
graph.addEdge(‘C‘,‘G‘);
graph.addEdge(‘D‘,‘G‘);
graph.addEdge(‘D‘,‘H‘);
graph.addEdge(‘B‘,‘E‘);
graph.addEdge(‘B‘,‘F‘);
graph.addEdge(‘E‘,‘I‘);


console.log(graph.toString());
/*
A->B  C  D  
B->A  E  F  
C->A  D  G  
D->A  C  G  H  
E->B  I  
F->B  
G->C  D  
H->D  
I->E  
*/
技術分享圖片

  那麽我們就實現了Graph類中最簡單的部分——如何添加頂點和邊。大家會不會覺得有點簡單了。嘿嘿.....有趣的還在後面,別急......

  好了,那麽到這裏這篇文章就結束了。下一篇文章我們再繼續學習圖的遍歷。

用js來實現那些數據結構15(圖01)

用js來實現那些數據結構15(圖01)