圖的定義
圖是由頂點的有窮非空集合和頂點之間邊的集合組成,通過表示為G(V,E),其中,G標示一個圖,V是圖G中頂點的集合,E是圖G中邊的集合。
無邊圖:若頂點Vi到Vj之間的邊沒有方向,則稱這條邊為無項邊(Edge),用序偶對(Vi,Vj)標示。
有向圖:若從頂點Vi到Vj的邊是有方向的,則成這條邊為有向邊,也稱為弧(Arc)。用有序對(Vi,Vj)標示,Vi稱為弧尾,Vj稱為弧頭。如果任意兩條邊之間都是有向的,則稱該圖為有向圖。
有向圖G2中,G2=(V2,{E2}),頂點集合(A,B,C,D),弧集合E2={<A,D>,{B,A},<C,A>,<B,C>}.
- 權(Weight):有些圖的邊和弧有相關的數(shù),這個數(shù)叫做權(Weight)。這些帶權的圖通常稱為網(wǎng)(Network)。
圖的表示
鄰接矩陣
- 說明
擁有n個頂點的圖,它所包含的連接數(shù)量最多是n(n-1)個;因此,要表達各個頂點之間的關聯(lián)關系,最清晰易懂的方式是使用二維數(shù)組(矩陣)。無向圖/有向圖:
* 頂點0和頂點1之間有邊關聯(lián),那么矩陣中的元素A[0][1]與A[1][0]的值就是1;
* 頂點1和頂點2之間沒有邊關聯(lián),那么矩陣中的元素A[1][2]與A[2][1]的值就是0。
* 矩陣從左上到右下的一條對角線,其上的元素值必然是0:任何一個頂點與它自身是沒有連接的。
* 無向圖對應的矩陣是一個對稱矩陣,因此A[m][n]和A[n][m]的值一定相等。
* 有向圖不再是一個對稱矩陣,,因此A[m][n]和A[n][m]的值不一定相等。
優(yōu)點:簡單直觀,可以快速查到一個頂點和另一頂點之間的關聯(lián)關系。
缺點:占用空間大,如果一個圖有1000個頂點,其中只有10個頂點之間有關聯(lián)(這種情況叫做稀疏圖),卻不得不建立一個1000X1000的二維數(shù)組。
鄰接表 和 逆鄰接表
-
鄰接表
為了解決鄰接矩陣占用空間的問題。鄰接表中,圖的每一個頂點都是一個鏈表的頭節(jié)點,其后連接著該頂點能夠直接達到的相鄰頂點。
* 鄰接表的存儲方式,占用的空間比鄰接矩陣要小得多;
* 想查出從頂點0能否到達頂點1,該怎么做呢?很簡單,我們從頂點0開始,順著鏈表的頭節(jié)點向后遍歷,看看后繼的節(jié)點中是否存在頂點1。
* 想查出頂點0能夠到達的所有相鄰節(jié)點,也很簡單,從頂點0向后的所有鏈表節(jié)點,就是頂點0能到達的相鄰節(jié)點。
* 要想查出有哪些節(jié)點能一步到達頂點1,這樣就麻煩一些了,我們要遍歷每一個頂點所在的鏈表,看看鏈表節(jié)點中是否包含節(jié)點1,最后發(fā)現(xiàn)頂點0和頂點3可以到達頂點1。
-
逆鄰接表
針對上述逆向查找的麻煩的問題,可以是用逆鄰接表來解決。逆鄰接表,和鄰接表是正好相反的。逆鄰接表每一個頂點作為鏈表的頭節(jié)點,后繼節(jié)點所存儲的是能夠直接達到該頂點的相鄰頂點。
要想查出有哪些節(jié)點能一步到達頂點1就容易了,從頂點1向后的所有鏈表節(jié)點,就是能一步到達頂點1的節(jié)點。
根據(jù)實際需求,選擇使用鄰接表還是逆鄰接表。
十字鏈表
同理,上述一個圖可能會存在要維護正反連個鄰接表。十字鏈表正好是將兩種結合起來。
十字鏈表的每一個頂點,都是兩個鏈表的根節(jié)點,其中一個鏈表存儲著該頂點能到達的相鄰頂點,另一個鏈表存儲著能到達該頂點的相鄰節(jié)點。
我們沒有必要把鏈表的節(jié)點都重復存儲兩次。在優(yōu)化之后的十字鏈表中,鏈表的每一個節(jié)點不再是頂點,而是一條邊,里面包含起止頂點的下標。
圖中每一條帶有黑色箭頭的鏈表,存儲著從頂點出發(fā)可到達的邊;每一條彩色箭頭的鏈表,存儲著進入頂點的邊。
圖的搜索
- 深度優(yōu)先遍歷:
也有稱為深度優(yōu)先搜索,簡稱DFS(Depth First Search)
。其實,就像是一棵樹的前序遍歷。它從圖中某個結點v出發(fā),訪問此頂點,然后從v的未被訪問的鄰接點出發(fā)深度優(yōu)先遍歷圖,直至圖中所有和v有 路徑相通的頂點都被訪問到。若圖中尚有頂點未被訪問,則另選圖中一個未曾被訪問的頂點作起始點,重復上述過程,直至圖中的所有頂點都被訪問到為止。
基本實現(xiàn)思想:
(1)訪問頂點v;
(2)從v的未被訪問的鄰接點中選取一個頂點w,從w出發(fā)進行深度優(yōu)先遍歷;
(3)重復上述兩步,直至圖中所有和v有路徑相通的頂點都被訪問到。
- 廣度優(yōu)先遍歷:
也稱廣度優(yōu)先搜索,簡稱BFS(Breadth First Search)
。BFS算法是一個分層搜索的過程,和樹的層序遍歷算法類同,它也需要一個隊列以保持遍歷過的頂點順序,以便按出隊的順序再去訪問這些頂點的鄰接頂點。
基本實現(xiàn)思想:
(1)頂點v入隊列。
(2)當隊列非空時則繼續(xù)執(zhí)行,否則算法結束。
(3)出隊列取得隊頭頂點v;訪問頂點v并標記頂點v已被訪問。
(4)查找頂點v的第一個鄰接頂點col。
(5)若v的鄰接頂點col未被訪問過的,則col入隊列。
(6)繼續(xù)查找頂點v的另一個新的鄰接頂點col,轉到步驟(5)。
直到頂點v的所有未被訪問過的鄰接點處理完。轉到步驟(2)。
廣度優(yōu)先遍歷圖是以頂點v為起始點,由近至遠,依次訪問和v有路徑相通而且路徑長度為1,2,……的頂點。為了使“先被訪問頂點的鄰接點”先于“后被訪問頂點的鄰接點”被訪問,需設置隊列存儲訪問的頂點。
下一節(jié)主要實現(xiàn):