【坐在馬桶上看算法】算法8:巧妙的鄰接表(數組實現)

        以前咱們介紹過圖的鄰接矩陣存儲法,它的空間和時間複雜度都是N2,如今我來介紹另一種存儲圖的方法:鄰接表,這樣空間和時間複雜度就都是M。對於稀疏圖來講,M要遠遠小於N2。先上數據,以下html

 

091650e0f00oqrcjcfnq93.png

4 5
1 4 9
4 3 8
1 2 5
2 4 6
1 3 7

 

        第一行兩個整數mn表示頂點個數(頂點編號爲1~n),m表示邊的條數。接下來m行表示,每行有3個數x y z表示頂點x到頂點y的邊的權值爲z下圖就是一種使用鏈表來實現鄰接表的方法。算法

091650gyll6hbqbjyxls8s.png

 

        上面這種實現方法爲圖中的每個頂點(左邊部分)都創建了一個單鏈表(右邊部分這樣咱們就能夠經過遍歷每一個頂點的鏈表,從而獲得頂點全部的邊了使用鏈表來實現鄰接表對於痛恨指針的的朋友來講,這簡直就是噩夢。這裏我將爲你們介紹一種使用數組來實現鄰接表這是一種在實際應用中很是容易實現的方法。這種方法爲每一個頂點ii1~n保存了一個相似鏈表的東西,裏面保存的是從頂點i出發的全部的邊,具體以下。數組

        首先咱們按照讀入的順序爲每一條邊進行編號1~m好比第一條邊「1 4 9」的編號就是1,「1 3 7」這條邊的編號是5spa

        這裏uvw三個數組用來記錄每條邊的具體信息,即u[i]v[i]w[i]表示第i條邊是從第u[i]號頂點到v[i]號頂點(u[i]àv[i],且權值爲w[i]指針

091650h35zq3wgx30x3oe3.png

        再用一個first數組來存儲每一個頂點其中一條邊的編號以便待會咱們來枚舉每頂點全部的邊(你可能存儲其中一條邊的編號就能夠了?不可能吧,每一個頂點都須要存儲其全部邊的編號才行吧!甭着急,繼續往下看好比1號頂點有一條邊是 「1 4 9」(該條邊的編號是1),那麼就將first[1]的值設爲1。若是某個頂點i沒有以該頂點爲起始點的邊,則將first[i]的值設爲-1。如今咱們來看看具體如何操做,初始狀態以下。code

091650zw3988qpj5iljj8g.png

 

        咦?上圖中怎麼多了一個next數組,有什麼做用呢?不着急,待會再解釋,如今先讀入第一條邊「1 4 9」。htm

        讀入第1條邊1 4 9,將這條邊的信息存儲到u[1]v[1]w[1]同時這條邊賦予一個編號由於這條邊是最早讀入的,存儲在uvw數組下標爲1的單元格中,所以編號就是1。這條邊的起始點是1號頂點,所以將first[1]的值設爲1blog

        另外這條編號爲1的邊1號頂點u[1]爲起始點第一條邊,因此next[1]的值設爲-1。也是說,若是當前這條編號爲i的邊,是咱們發現的u[i]起始點的第一條邊,就將next[i]的值設爲-1(貌似的這個next數組很神祕啊⊙_get

091651kwo5g0aycy07wfwd.png

 

        讀入第2條邊4 3 8將這條邊的信息存儲到u[2]v[2]w[2]中,這條邊的編號爲2這條邊的起始頂點是4號頂點,所以將first[4]的值設爲2另外這條「編號爲2的邊」是咱們發現以4號頂點爲起始點的第一條邊,因此將next[2]的值設爲-1class

091651fkswcj34c05k8w4k.png

 

        讀入第3條邊1 2 5將這條邊的信息存儲到u[3]v[3]w[3]中,這條邊的編號爲3,起始頂點是1號頂點。咱們發現1號頂點已經有一條編號爲的邊了,若是此時將first[1]的值設爲3那「編號爲1的邊豈不是丟失了?我有辦法,此時只需將next[3]的值設爲1便可。如今你知道next數組是用來作什麼的吧。next[i]存儲的是「編號爲i的邊」的「一條邊」的編號。

091651df28foy9ct7fl7qf.png

 

        讀入第4條邊(2 4 6),將這條邊的信息存儲到u[4]v[4]w[4]中,這條邊的編號爲4起始頂點是2號頂點,所以將first[2]的值設爲4。另外這條「編號爲4的邊」是咱們發現以2號頂點爲起始點的第一條邊,因此將next[4]的值設爲-1

091652vf4eg69f5zfsese9.png

 

        讀入第5條邊(1 3 7),將這條邊的信息存儲到u[5]v[5]w[5]中,這條邊的編號爲5,起始頂點1號頂點。此時須要first[1]的值設爲5並將next[5]的值改成3

091652li6mzammza242tmp.png

 

        此時,若是咱們想遍歷1號頂點的每一條邊就很簡單1號頂點的其中一條邊的編號存儲在first[1]中。其他的邊則能夠經過next數組尋找到請看下圖

091652rtjh5qe2211eee58.png

        細心的同窗會發現,此時遍歷邊某個頂點邊的時候的遍歷順序正好與讀入時候的順序相反。由於爲每一個頂點插入邊的時候都直接插入鏈表的首部而不是尾部。不過這並不會產生任何問題,這正是這種方法的其妙之處

 

        建立鄰接表的代碼以下

 
int n,m,i;
//u、v和w的數組大小要根據實際狀況來設置,要比m的最大值要大1
int u[6],v[6],w[6];
//first和next的數組大小要根據實際狀況來設置,要比n的最大值要大1
int first[5],next[5];
scanf("%d %d",&n,&m);
//初始化first數組下標1~n的值爲-1,表示1~n頂點暫時都沒有邊
for(i=1;i<=n;i++)
    first[i]=-1;
for(i=1;i<=m;i++)
{
    scanf("%d %d %d",&u[i],&v[i],&w[i]);//讀入每一條邊
    //下面兩句是關鍵啦
    next[i]=first[u[i]];
    first[u[i]]=i;
}

 

        接下來如何遍歷每一條邊呢?咱們以前說過其實first數組存儲的就是每一個頂點ii1~n)的第一條邊。好比1號頂點的第一條邊是編號爲5的邊(1 3 7),2號頂點的第一條邊是編號爲4的邊(2 4 6),3號頂點沒有出向邊,4號頂點的第一條邊是編號爲2的邊(2 4 6)。那麼如何遍歷1號頂點的每一條邊呢?也很簡單。請看下圖:

 

        遍歷1號頂點全部邊的代碼以下。

 
k=first[1];// 1號頂點其中的一條邊的編號(其實也是最後讀入的邊)
while(k!=-1) //其他的邊均可以在next數組中依次找到
{
    printf("%d %d %d\n",u[k],v[k],w[k]);
    k=next[k];
}

 

        遍歷每一個頂點的全部代碼以下

for(i=1;i<=n;i++)
{
    k=first[i];
    while(k!=-1)
    {
        printf("%d %d %d\n",u[k],v[k],w[k]);
        k=next[k];
    }
}

 

        能夠發現使用鄰接表來存儲圖的時間空間複雜度是O(M),遍歷每一條邊的時間複雜度是也是O(M)。若是一個圖是稀疏圖的話,M要遠小於N2。所以稀疏圖選用鄰接表來存儲要比鄰接矩陣來存儲要好不少。

        歡迎轉載,碼字不容易啊,轉載麻煩註明出處

        【啊哈!算法】系列8:巧妙的鄰接表(數組實現)  http://www.cnblogs.com/ahalei/p/3651334.html

相關文章
相關標籤/搜索