數據結構 —— 圖

樹是一種非線性表的數據結構,圖也是,圖和樹比起來,是一種更加複雜的非線性表數據結構。

涉及圖的算法有很多,也非常複雜,比如圖的搜索、最短路徑、最小生成樹、二分圖等等。

 

樹中的元素稱爲節點,而圖中的元素稱爲頂點。

圖中的一個頂點可以與任意其他頂點建立連接關係。

這種建立的關係叫作邊(edge)。

 

 

微博、微信等這些社交網絡的好友關係就是一個非常典型的圖結構。

 

如果兩個用戶之間互加好友,那就在兩者之間建立一條邊。所以,整個微信的好友關係就可以用一張圖來表示。

其中,每個用戶有多少個好友就對應到跟頂點相連接的邊的條數,就叫做頂點的度(degree)。

 

有向圖和無向圖

 

實際上,有些社交軟件不同,比如微博。微博允許單向關注,也就是說,用戶 A 關注了用戶 B,但用戶 B 可以不關注用戶 A。

 

用圖也可以表示這種單向的社交關係。其實邊是可以有方向的

 

如果用戶 A 關注了用戶 B,我們就在圖中畫一條從 A 到 B 的帶箭頭的邊,來表示邊的方向。

如果用戶 A 和用戶 B 互相關注了,那我們就畫一條從 A 指向 B 的邊,再畫一條從 B 指向 A 的邊。

我們把這種邊有方向的圖叫作“有向圖”。把邊沒有方向的圖就叫作“無向圖”。

 

 

在有向圖中,我們把度分爲入度(In-degree)和出度(Out-degree)。

頂點的入度,表示有多少條邊指向這個頂點;

頂點的出度,表示有多少條邊是以這個頂點爲起點指向其他頂點。

 

就像是微博,入度就表示有多少粉絲,出度就表示關注了多少人。

 

帶權圖

 

還有一種社交軟件:QQ,QQ有一個親密度的功能。

QQ 不僅記錄了用戶之間的好友關係,還記錄了兩個用戶之間的親密度。

如果兩個用戶經常往來,那親密度就比較高;如果不經常往來,親密度就比較低。

 

要想記錄圖中記錄這種好友關係的親密度,就要用到帶權圖帶權圖(weighted graph)。

 

在帶權圖中,每條邊都有一個權重(weight),我們可以通過這個權重來表示 QQ 好友間的親密度。

 

 

圖的儲存方法

 

 鄰接矩陣(Adjacency Matrix)

鄰接矩陣是圖最直觀的一種存儲方法。

鄰接矩陣的底層依賴一個二維數組。

對於無向圖來說,如果頂點 i 與頂點 j 之間有邊,我們就將 A[i][j]和 A[j][i]標記爲 1;

對於有向圖來說,如果頂點 i 到頂點 j 之間,有一條箭頭從頂點 i 指向頂點 j 的邊,那我們就將 A[i][j]標記爲 1。

對於帶權圖,數組中就存儲相應的權重。

 

 

缺點

用鄰接矩陣來表示一個圖,雖然簡單、直觀,但是比較浪費存儲空間。

 

對於無向圖來說,如果 A[i][j]等於 1,那 A[j][i]也肯定等於 1。實際上,我們只需要存儲一個就可以了。

也就是說,無向圖的二維數組中,如果我們將其用對角線劃分爲上下兩部分。

那我們只需要利用上面或者下面這樣一半的空間就足夠了,另外一半白白浪費掉了。

 

另外如果,如果我們存儲的是稀疏圖(Sparse Matrix),也就是說,頂點很多,但每個頂點的邊並不多。

那鄰接矩陣的存儲方法就更加浪費空間了。

 

比如微信有好幾億的用戶,對應到圖上就是好幾億的頂點。但是每個用戶的好友並不會很多,一般也就三五百個而已。

如果我們用鄰接矩陣來存儲,那絕大部分的存儲空間都被浪費了。

 

優點

鄰接矩陣的存儲方式簡單、直接,因爲基於數組,所以在獲取兩個頂點的關係時,就非常高效。

用鄰接矩陣存儲圖的另外一個好處是方便計算。

這是因爲,用鄰接矩陣的方式存儲圖,可以將很多圖的運算轉換成矩陣之間的運算。

 

鄰接表(Adjacency List)

 

鄰接表有點像散列表,每個頂點對應一條鏈表,鏈表中存儲的是與這個頂點相連接的其他頂點。

圖中是一個有向圖 的鄰接表存儲方式,每個頂點對應的鏈表裏面,存儲的是指向的頂點。

 

對於無向圖來說也是類似的,不過,每個頂點的鏈表中存儲的是跟這個頂點有邊相連的頂點。

 

如果我們要確定,是否存在一條從頂點 2 到頂點 4 的邊,那我們就要遍歷頂點 2 對應的那條鏈表,看鏈表中是否存在頂點 4。

比起鄰接矩陣的存儲方式,在鄰接表中查詢兩個頂點之間的關係就沒那麼高效了。

 

鄰接矩陣存儲起來比較浪費空間,但是使用起來比較節省時間。

相反,鄰接表存儲起來比較節省空間,但是使用起來就比較耗時間。

 

鄰接表長得很像散列表,在基於鏈表法解決衝突的散列表中,如果鏈過長的話。

爲了提高查找效率,我們可以將鏈表換成其他更加高效的數據結構,比如平衡二叉查找樹等。

我們也可以將鄰接表同散列表一樣進行“改進升級”。我們可以將鄰接表中的鏈表改成平衡二叉查找樹。比如紅黑樹

我們就可以更加快速地查找兩個頂點之間是否存在邊了。

二叉查找樹可以換成其他動態數據結構,比如跳錶、散列表等。

除此之外,我們還可以將鏈表改成有序動態數組,可以通過二分查找的方法來快速定位兩個頂點之間否是存在邊。

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章