1. 程式人生 > >鏈式前向星(系列)

鏈式前向星(系列)

看名字很難,實際上賊簡單...

本蒟蒻瑟瑟發抖,請大佬們不要噴...

寫的有點長,如果覺得讀不下去...

肯定讀的下去,我寫得多通俗易懂啊!

不要管右面的表格...看左面的圖...好,讓我們開始:

PS:n代表點的個數,m代表邊的個數

 

Part 1:鄰接矩陣

先列出來矩陣,然後慢慢的講

          v0      v1      v2      v3      v4      v5      v6      v7      v8

v0      1        1        0        0        0       1        0        0        0

v1      1        1        1        0        0       0        1        0        1 

v2      0        1        1        1        0       0        0        0        1 

v3      0        0        1        1        1       0        0        1        1 

v4      0        0        0        1        1       1        0        1        0 

v5      1        0        0        0        1       1        1        0        1 

v6      0        1        0        1        0       1        1        1        0

v7      0        0        0        1        1       0        1        1        0

v8      0        1        1        1        0       0        0        0        1 

(1,0可以用連線這兩個點的邊權代替)

有橫列,有縱列,交點代表橫軸和縱軸所代表的點能否直接聯通

直接聯通的意思就是有一條邊直接連著它們而不是通過別的點與邊相連

0代表不能直接聯通,而1代表能直接聯通

好啦,現在如果要判斷某兩個點能否聯通,開始搜尋即可

找能和點1聯通的所有點然後接著搜即可...(時間複雜度為n的平方)

或者使用並查集(合併查詢)也可以...

但是很慢,真的很慢,時空複雜度都很大...

 

好啦,那我們該怎麼辦呢?有請Part 2

Part 2 鄰接表

它可以大大地減小時間複雜度(時間複雜度為m)

但是減小不了空間複雜度...

先列出來表,然後慢慢的講

v0      v1      v2      v3      v4      v5      v6      v7      v8

v0      v0      v1      v2      v3      v0      v1      v3      v1

v1      v1      v2      v3      v4      v4      v3      v4      v2

v5      v2      v3      v4      v5      v5      v5      v6      v3

          v6      v8      v7      v7      v6      v6      v7      v8

          v8                v8                v8      v7

第一行代表每個點

第一行點下面的點代表它能直接聯通的點

你看,搜每個點鄰接矩陣必須搜七次

而鄰接表只需要搜三到四次即可

大大減少了時間複雜度

尤其是用BFS搜點的時候,所需的時間複雜度更小

但是空間複雜度仍然很大...

小心開long long會原地爆炸...

所以,有請Part 3

 

Part 3 鏈式前向星

時間複雜度與鄰接表一樣,均為m

但它大大優化了空間複雜度

(不要笑話我只會用“大大的”這種形容詞)

那麼它的原理是什麼呢?

讓我們代入情景:

我們要去一個世界頂級的珠寶商場

賣的東西很好,當然也很貴

但我們的錢包比自己的臉還乾淨

那怎麼辦呢?怎麼辦呢?

閉上雙眼!兩隻手放在膝蓋上!默唸三遍:

我很有錢!

我很有錢!

我很有錢!

好了,我現在很有錢!

穿著你媽你(阿瑪尼),挎著卡地亞

我們走了進去,領到了一張很小的紙(只能寫一個人的名字)

(這個商店一個人一次只能購買一件珠寶)

(而且入場費灰常貴)

一看,啊!我的純鈦合金24K大狗眼被珠寶的閃光晃瞎了!

揹著手信步前進,我要買這個!那個更好!這個太棒了!

但是珠寶上也有一張很小的紙(只能寫一個人的名字)

而且這張紙上已經有別人的名字了

那我們應該怎麼辦?

把他的名字擦了,寫上我的名字!

我覺得持這種想法的人你很壞

但你確實對了,不過只對了一半

還少一步:在你的紙上寫下你擦掉的人的名字

這樣我們才有誠信嘛,對不對?

好啦,假設有1,7,9三個人買同樣的珠寶

主辦方一看紙上有9的名字

就去問9,說你擦了誰的?

9說我擦了7的

主辦方再屁顛屁顛地去問7,說你擦了誰的

7說我擦了1的

主辦方去問1,1說我沒有擦別人的

好啦,搜尋到此為止,1,7,9三個人買了這個珠寶

主辦方把三個珠寶包起來送給他們就行啦!

這就是鏈式前向星的基本原理!

我們會開一個結構體和一個數組

struct Edge{

    int next;

    int to;

    int x;

}edge[(自己寫大小)];

head[(自己寫大小)];

int cnt;

next存的是當前邊所指向的下一條邊

to存的是當前邊所指向的目標節點

w存的是當前邊的權值

edge[m]表示第m個邊

head[m]表示第m個點的最後一條邊,以確定尋找的邊界

cnt存的是總邊數

這樣一個神奇的圖就建出來啦!

建圖操作:

void add(int x,int y){

    edge[++cnt].to = y;

    edge[cnt].nxt = head[x];

    head[x] = cnt;

}(可以雙向建圖)

那麼這個操作什麼意思呢?

看這個圖!(所有陣列從1開始)

(to與next有m個元素,因為有m個邊)

(head有n個元素,因為有n個邊)

(字首v是用來理解的,當你懂了以後可以把v去掉)

比如要在v1與v8之間連一條邊

由v8指向v1

那麼需要add(v8,v1)

此時cnt = 1

首先要在to[1]記憶體一個v1(就是數字1)

此時head[v8] = 0(就是陣列第8位)

則next[1] = 0

head[v8] = 1

to[v1 0 0 0 0 0 0 0 0 0。。。](後面的元素用省略號代替)

next[0 0 0 0 0 0 0 0 0 0。。。]

head[0 0 0 0 0 0 0 1 。。。]

雙向建圖:

此時cnt = 2

首先要在to[2]記憶體一個v8

此時head[v1] = 0

則next[2] = 0

head[v1] = 2

to[v1 v8 0 0 0 0 0 0 0 0。。。]

next[0 0 0 0 0 0 0 0 0 0。。。]

head[2 0 0 0 0 0 0 1 。。。]

再在v1與v2之間連一條邊

由v2向v1

那麼需要add(v2 ,v1)

此時cnt = 3

首先要在to[3]記憶體一個v1

此時head[v2] = 0

則next[3] = 0

head[v2]  = 3

to[v1 v8 v1 0 0 0 0 0 0 0。。。]

next[0 0 0 0 0 0 0 0 0 0。。。]

head[2 3 0 0 0 0 0 1 。。。]

雙向建圖:

此時cnt = 4

首先要在to[4]記憶體一個v2

此時head[v1] = 2

則next[4] = 2

head[v1] = 4

to[v1 v8 v1 v2 0 0 0 0 0 0。。。]

next[0 0 0 2 0 0 0 0 0 0。。。]

head[4 3 0 0 0 0 0 1 。。。]

什麼意思呢?

head與next裡存的是數的編號

to裡存的是真實的點

真實的點在to裡的編號(比如v8編號為2,v2編號為4)

就是head數組裡的另一個被它擦掉的數在to裡的編號

(就相當於被你擦掉並寫在你手裡的紙上的編號)

先舉這麼多栗子(例子)

 當我要查詢v1直接連線的邊(初邊)的時候

先查head[v1] = 4

那麼就查to[head[v1]]即to[4] = v2

證明v2與v1相連

v2在to裡的編號是4

再找next[4] = 2

即在v2前面被擦掉的數在to裡的編號是2

那麼就查to[2] = v8

證明v8與v1相連

v8在to裡的編號是2

再找next[2] = 0

意思就是沒有數被它擦掉了

搜尋結束

與v1相連的點輸入了v2,v8

查詢的結果也是v2,v8正確

 

那麼鏈式前向星的思想與程式碼就講完了!

感謝lyn大佬與zyr大佬的幫助!

小夥伴們都懂了嗎?

 

學習資訊競賽的路還很長,但要堅持下去。

要堅信:真金不怕火煉!

                                      ——沃茲基碩德

                                          (我自己說的)