圖是非線性數據結構,是一種較線性結構和樹結構更爲複雜的數據結構,在圖結構中數據元素之間的關係能夠是任意的,圖中任意兩個數據元素之間均可能相關。java
圖(graph)是一種網狀數據結構,圖是由非空的頂點集合和一個描述頂點之間關係的集合組成。算法
二元組定義:G=(V,E)數組
若是圖的邊限定爲從一個頂點指向另外一個頂點,即每條邊都是頂點的有序偶對,稱之爲有向圖(directed graph)。網絡
方向起始的頂點稱爲起點或尾(弧尾)。方向指向的頂點稱爲終點或頭(弧頭)。數據結構
若是圖中的邊沒有方向性,即每條邊都是頂點的無序偶對,稱之爲無向圖(undirected graph)。ui
設圖G=(V,E)和圖G'=(V',E')。若是V’⊆V且E’⊆E,則稱G'是G的一個子圖(subgraph)。 若是V’=V且E’⊆E,則稱G’是G的一個生成子圖(spanning subgraph)。spa
有n(n-1)/2條邊的無向圖稱爲無向徹底圖。.net
有n(n-1)條邊的有向圖稱爲有向徹底圖。3d
有不多邊的圖稱爲稀疏圖。指針
邊較多的圖稱爲稠密圖。
對於無向圖G=(V,E),若是邊(u,v)∈E,則稱頂點u與頂點v互爲鄰接點。
兩個鄰接點連成的邊叫作兩個節點的相關邊。
與每一個頂點相連的邊的數叫該點的度(degree)。
對有向圖中某結點的弧頭數稱爲該結點的入度(in degree)。
對有向圖中某結點的弧尾數稱爲該結點的出度(out degree)。
有度=入度+出度
在一個圖中,某個頂點Vp出發,沿着一些邊通過一些頂點,到達Vg則稱通過的這些頂點序列爲Vp到Vg的路徑。Vp是路徑的始點,Vg是路徑的終點。
對於有向圖路徑也是有向的,路徑的方向只能是從起點到終點,且與它通過的每一條邊的方向一致。
路徑上的邊或弧的數目稱之爲該路徑的長度。
除始點和終點外,其餘各頂點均不一樣的路徑稱之爲簡單路徑。
從一個頂點出發又回到該頂點,則所通過的路徑稱爲迴路。
始點和終點相同的簡單路徑稱之爲簡單迴路。
在無向圖中,從一個頂點到另外一個頂點之間有路徑,則稱這兩個頂點是連通的。
若是圖中任意一對頂點之間都是連通的,則稱此圖爲連通圖。
非連通圖中的每個連通部分叫連通份量。
對於有向圖,若兩點之間有互相到達的路徑,則稱這兩點是強連通。
若是有向圖中任何一對頂點都是強連通的,則此圖叫強連通圖。
有向圖中最大連通子圖稱爲有向圖的強連通份量。
有些圖對應每條邊有一相應的數值,這個數值稱爲該邊的權。
帶權的圖稱爲網(network)。網可分爲有向網和無向網。
以下是圖的抽象數據類型定義:
ADT Graph{ 數據對象D:D是具備相同性質的數據元素的集合。 數據關係R:R={<u,v>|P(u,v)∧(u,v∈D)} 基本操做: getType(); //返回圖的類型 getVexNum(); //返回圖中頂點數 getEdgeNum(); //返回圖中邊數 getVertex(); //返回圖中全部頂點的迭代器 getEdge(); //返回圖中全部邊的迭代器 remove(v); //在圖中刪除特定的頂點 remove(e); //在圖中刪除特定的邊 insert(v); //在圖的頂點集中添加一個新頂點 insert(e); //在圖的邊集中添加一條新邊 areAdjacent(u,v); //判斷v是不是u的鄰接點 edgeFromTo(u,v); //返回u到v的邊,若是不存在返回空 adjVertexs(u); //返回頂點u的全部鄰接點 DFSTraverse(v); //從頂點v開始深度優先搜索遍歷圖 BFSTraverse(v); //從頂點v開始廣度優先搜索遍歷圖 shortestPath(v); //求頂點v到圖中全部頂點的最短路徑 generateMST(); //求無向圖的最小生成樹,有向圖不支持此操做 toplogicalSort(); //求有向圖的拓撲序列。無向圖不支持此操做 criticalPath(); //求有向無環圖的關鍵路徑。無向圖不支持此操做 }ADT Graph
從圖的邏輯結構定義來看,沒法將圖中的頂點排列成一個惟一的線性序列。在圖中任意一個頂點均可以當作是圖的第一個頂點,對任何一個頂點而言,它的鄰接點之間也不存在順序關係。爲了方便存儲和操做,須要將圖中的頂點按必定的序列排列起來。
頂點在圖中的位置就是指該頂點在人爲肯定的序列中的位置。
因爲圖的結構比較複雜,任意兩個頂點之間均可能存在聯繫,所以沒法以數據元素在存儲區的位置來表示元素之間的關係,即圖沒有順序映像的存儲結構,但能夠藉助數組來表示數據元素之間的關係。
藉助數組存儲的方法有鄰接矩陣表示法和鄰接表表示法。
圖的鄰接矩陣(adjacent matrix)表示法是使用數組來存儲圖結構的方法,也被稱爲數組表示法。 它採用兩個數組來表示圖:一個是用於存儲全部頂點信息的一維數組,另外一個是用於存儲圖中頂點之間關聯關係的二維數組,這個關聯關係數組也被稱爲鄰接矩陣。
鄰接矩陣有以下特性:
優勢:
鄰接矩陣表示法對於以圖的頂點爲主的運算比較適合。
缺點:
除徹底圖外,其餘圖的鄰接矩陣有許多零元素,特別是當n值較大,而邊數相對徹底圖的邊n-1又少的多時,則此矩陣稱爲稀疏矩陣,很是浪費存儲空間。
鄰接表(adjacency list)是圖的一種鏈式存儲方法,鄰接表表示法相似於樹的孩子鏈表表示法。
在鄰接表中對於圖G中的每一個頂點vi創建一個單鏈表,將全部鄰接於vi的頂點vj鏈成一個單鏈表,並在表頭附設一個表頭結點,這個單鏈表就稱爲頂點vi的鄰接表。
鄰接表中共有兩種結點結構,分別是邊表結點和表頭結點。
鄰接表中的每個結點均包含有兩個域:鄰接點域和指針域。
邊表結點由3個域組成:
頭結點由2個域組成:
以下圖爲鄰接表的存儲示例:
在無向圖的鄰接表中,頂點的每個邊表結點對應於與頂點相關聯的一條邊。
在有向圖的鄰接表中,頂點的每個邊表結點對應於以頂點爲始點的一條弧,所以也稱有向圖的鄰接表的邊表爲出邊表。
在有向圖的鄰接表中,將頂點的每一個邊表結點對應於以頂點爲重點的一條弧,即用便捷點的鄰接點域存儲鄰接到頂點的序號,由此構成的鄰接表稱爲有向圖的逆鄰接表,逆鄰接表有邊表稱爲入邊表。
鄰接表與鄰接矩陣的關係以下:
鄰接表表示法示例以下:
鄰接表的性質以下:
須要說明的是:
圖的另外一種矩陣表示法爲以頂點和邊的關聯關係爲基礎創建矩陣,這個矩陣稱之爲關聯矩陣。定義以下:
圖G=(V,E)的關聯矩陣是一個|V|×|E|矩陣,使得:
在一個多圖的關聯矩陣中,一些列是相同的,一個列只有一個1則表明一個環。 以下是關聯矩陣的表示:
從圖中某個頂點出發訪問圖中全部頂點,且使得每一頂點僅被訪問一次,這一過程稱之爲圖的遍歷。
圖的遍歷是圖的運算中最重要的運算,圖的許多運算均以遍歷爲基礎。
圖的遍歷按搜索路徑不一樣分爲深度優先搜索遍歷(Depth First Search)和廣度優先搜索遍歷(Breadth First Search)。
深度優先搜索的基本方法是:
從圖中某個頂點發v出發,訪問此頂點,而後依次從v的未被訪問的鄰接點出發深度優先遍歷圖,直至圖中全部和v有路徑相通的頂點都被訪問到;若此時圖中尚有頂點未被訪問,則另選圖中一個不曾被訪問的頂點做起始點,重複上述過程,直至圖中全部頂點都被訪問到爲止。
以下圖:
圖的深度優先搜索遍歷是一個遞歸過程,其特色是儘量先對縱深方向的頂點進行訪問。
對圖進行深度優先搜索遍歷時,按訪問頂點的前後次序所獲得的頂點序列稱爲該圖的深度優先搜索遍歷序列,簡稱爲DFS序列。
一個圖的DFS序列不必定唯一,這與算法、圖的存儲結構以及初始出發點有關。
圖的深度優先搜索算法也可使用堆棧以非遞歸的形式實現,使用堆棧實現深度優先搜索的思想以下:
廣度優先搜索遍歷的基本方法是:
假設從圖中某頂點v出發,在訪問了v以後依次訪問v的各個不曾訪問過的鄰接點,而後分別從這些鄰接點出發依次訪問它們的鄰接點,並使「先被訪問的頂點的鄰接點」先於「後被訪問的頂點的鄰接點」先被訪問,直至圖中全部已被訪問的頂點的鄰接點都被訪問到。若此時圖中尚有頂點未被訪問,則另選圖中一個不曾被訪問的頂點做起始點,重複上述過程,直至圖中全部頂點都被訪問到爲止。
過程以下圖:
圖的廣度優先搜索遍歷是一個遞歸過程,其特色是儘量先對橫向的頂點進行訪問。
對圖進行廣度優先搜索遍歷時,按訪問頂點的前後次序所獲得的頂點序列,稱爲該圖的廣度優先搜索遍歷序列,簡稱爲BFS序列。
一個圖的BFS序列不必定唯一,這與算法、圖的存儲結構以及初始出發點有關。
廣度優先搜索遍歷的實現與樹的按層遍歷實現同樣都須要使用隊列,使用隊列實現廣度優先搜索的思想以下:
圖論中,一般將樹定義爲一個無迴路連通圖。對於無迴路連通圖,只要選定某個頂點做爲根,以此頂點爲樹根對每條邊定向,竟能獲得一般的樹。
一個連通圖G的子圖若是是一棵包含G的全部頂點的樹,則該子圖稱爲G的生成樹。
生成樹有一下特色:
由深度優先搜索獲得的生成樹稱爲深度優先生成樹,簡稱爲DFS生成樹。
由廣度優先搜索獲得的生成樹稱爲廣度優先生成樹,簡稱爲BFS生成樹。
由圖的遍歷可得以下概念:
若從圖的某個頂點出發,能夠系統地遍歷圖中的全部頂點,則遍歷時,通過的邊和圖的全部頂點所構成的子圖,稱爲圖的生成樹。
對於連通網絡G=(V,E),邊是帶權的,於是G的生成樹的各邊也是帶權的。生成樹的各邊的權值總和稱爲生成樹的權,並把權最小的生成樹稱爲G的最小生成樹。
構成最小生成樹的方法有多種。這些算法能夠分爲下面幾類:
不管上述那種類型的算法,均用到了最小生成樹的以下性質。
設G=(V,E)是一個連通網絡,U是頂點集V的一個真子集。若是(u,v)是G中全部的一個端點在U(即u∈U)裏,另外一個端點不在U(即v∈V-U)裏的邊中,具備最小權值的一條邊,則必定存在G的一棵最小生成樹包括此邊(u,v)。這個性質稱爲MST性質。
設G(V,E)爲一個連通網,頂點集V=(v1,v2,……,vn)。設T(U,TE)是所要求的G的一棵最小生成樹,其中U是T的頂點集,TE是T的邊集,而且將G中邊上的權看做是長度。
普里姆算法的基本思想:
首先任選V中一個頂點v1,構成入選頂點集U={v1},此時入選邊集TE爲空集,V中剩餘頂點構成待選頂點集V-U;在全部關聯於入選頂點集和待選頂點集的邊中選取權值最小的一條邊(vi,vj)加入入選邊集(這裏vi爲入選頂點,vj爲待選頂點),同時將vj加入入選定點集。重複以上過程,直至入選頂點集U包含全部頂點(U=V),入選邊集包含n-1條邊,MST性質保證上述過程求得的T(U,TE)是G的一棵最小生成樹。
過程以下圖:
設G=(V,E)是連通網絡,令最小生成樹的初始狀態爲只有n個頂點而無邊的非連通圖T=(V,Φ),T中的每一個頂點自成一格連通份量。按照長度遞增的順序依次選擇E中的邊(u,v),若是該邊斷點u、v分別是當前T的兩個連通份量T一、T2中的頂點,則將該邊加入到T中,T一、T2也由此邊鏈接成一格連通份量;若是u、v是當前同一個連通份量中的頂點,則捨去此邊,這是由於每一個連通份量都是一棵樹,此邊添加到樹中將造成迴路。依次類推,知道T中全部頂點都在同一連通份量上爲止。從而獲得G的一棵最小生成樹T。
過程以下:
克魯斯卡爾算法和普里姆算法產生的生成樹是相同的。
不一樣之處:
在許多應用領域,帶權圖都被用來描述某個網絡,好比通訊網絡、交通網絡等。這種狀況下,各邊的權重就對應於兩點之間通訊的成本或交通費用。此時,一類典型的問題就是:在任意指定的兩點之間若是存在通路,那麼最小的消耗是多少。這類問題實際上就是帶權圖中兩點之間最短路徑的問題。
在圖中兩點之間的最短路徑問題包括兩個方面:一是求圖中一個頂點到其餘頂點的最短路徑,二是求圖中每對頂點之間的最短路徑。
這裏的路徑不是指路徑上邊數的總和,而是指路徑上各邊的權值總和。
單源最短路徑是指,在帶權圖G=(V,E)中,已知源點爲s∈V,求s到其他各頂點的最短路徑。
若π=(u0=s,u1,u2,… ,uk=v)是從頂點s到頂點v的最短路徑,則對於任何0 ≤ i < j ≤ k,τ=(ui, ui+1, … , uj)是從頂點ui到uj的最短路徑。
此定理也能夠簡單的描述爲:最短路徑的子路徑也是最短路徑。
算法基本思想:
設置兩個頂點集S和T,S中存放已肯定最短路徑的頂點,T中竄訪待肯定最短路徑的頂點。初始時,S中僅有一個源點,T中包含除源點外其他頂點,此時各頂點的當前最短路徑長度爲源點到該頂點的弧上的權值。接着選取T中當前最短路徑長度最小的一個頂點v加入S,而後修改T中生於頂點的當前最短路徑長度。
修改原則是:當v的最短路徑長度是v到T中的頂點之間的權值之和小於該頂點的當前最短路徑長度時,用前者替換後者。重複上述過程,直至S中包含全部的頂點。
Dijkstra算法只能求出源點到其他頂點的最短路徑,若是須要求出帶權圖中任意一對頂
點之間的最短路徑,能夠用每個頂點做爲源點,重複調用Dijkstra算法|V|次,時間複雜度爲O(|V|^3)。
假設求出的每對頂點之間的最短距離使用一個|V|×|V|矩陣D保存和輸出。下面定義符號D(k),0 ≤k ≤|V|。在定義中假設帶權圖中全部的頂點排成一個序列。
定義:D(k)(0 ≤k ≤|V|)是一個|V|階方陣,其中D(k)[i][j]是在考慮帶權圖中前k個頂點,將它們做爲中間頂點時從頂點vi到頂點vj的當前最短距離(1 ≤i ≤|V|,1 ≤j ≤|V|)。
D(0)表示當頂點vi,vj之間不考慮任何頂點做爲中間頂點時的最短距離,顯然D(0)[i][j]就是頂點vi到vj的邊的權值,若是使用鄰接矩陣A做爲存儲結構,D(0)[i][j]=A[i][j].weight。而且若是將全部的頂點均考慮在內,vi到vj的當前最短距離就是在圖中vi到vj的最短距離,即δ(vi,vj) = D(|V|)[i][j] (1 ≤i ≤|V|,1 ≤j ≤|V|)。
Floyd算法的基本思想是:
(1)用鄰接矩陣初始化D(0),對角線元素爲0;
(2)在頂點vi、vj之間考慮頂點v1,比較在引入v1以後vi到vj的當前最短距離是否能夠經過v1變得更小。把v1放在vi到vj的路徑上,vi到vj之間可能會產生新的路徑,其距離爲D(0)[i][1] + D(0)[1][j],固然v1的引入可能反而會加大vi到vj的距離,所以須要比較D(0)[i][1] + D(0)[1][j]與D(0)[i][j]的大小。最終,在考慮v1以後vi到vj的當前最短距離爲二者中小的。即:
D(1)[i][j] = min{ D(0)[i][1] + D(0)[1][j] , D(0)[i][j]}
(3)通常狀況下,若是在考慮了前k-1個頂點{v1, v2, … , vk-1}以後,從頂點vi到vj的當前最短距離是D(k-1)[i][j]。那麼在頂點vi、vj之間考慮前k個頂點時,頂點vi到vj的當前最短距離爲如下兩個距離中小的:在考慮前k-1個頂點基礎上將vk放在vi到vj的路徑上,此時產生新的路徑長度爲D(k-1)[i][k] + D(k-1)[k][j];以及不將vk放在vi到vj的路徑上的距離D(k-1)[i][j]。最終,在考慮前k個頂點{v1, v2, … , vk}以後,vi到vj的當前最短距離 D(k)[i][j] = min{ D(k-1)[i][k] + D(k-1)[k][j] , D(k-1)[i][j]} 1 ≤k ≤|V|
(4)依次進行下去,直到k = |V|。此時D(|V|)[i][j]即爲帶權圖中任意兩個頂點vi到vj的最短距離。
有向無環圖(directed acyclic graph)是指一個無環的有向圖,簡稱DAG。
用頂點表示活動(Activity),用弧表示活動之間的前後次序關係的有向圖,稱爲頂點活動圖(Activity On Vertex Network),簡稱爲AOV網。
AOV網的特色是在網中必定不能有有向迴路。檢測網中是否存在環,則採用拓撲排序的方法。
在一個AOV網絡中,若vi爲vj的先行活動,vj爲vk的先行活動,則vi必爲vk的先行活動,即活動的先行關係具備傳遞性。
若是從離散數學的觀點看AOV網絡中的活動關係能夠當作是一個偏序關係,工程完成活動的線性序列能夠當作是一個全序關係。
由某個集合上的一個偏序獲得該集合上的一個全序,此操做稱之爲拓撲排序(topological sort)。即將AOV網絡各個頂點(表明各個活動)排列成一個線性有序的序列,使得AOV網絡中全部應存在的前驅和後繼關係都能獲得知足。拓撲排序就是構造AOV網絡頂點的拓撲有序序列的運算。
拓撲排序算法的基本步驟是:
與AOV網絡對應的是邊表示活動的AOE網絡。若是在有向無環的帶權圖中:
因爲一個工程只有一個開始點和一個完成點,因此在正常狀況下,AOE網絡中只有一個入度爲0的頂點,也只有一個出度爲0的頂點,它們分別稱之爲源點和匯點。
在AOE網絡中,有些活動順序進行,有些活動並行進行。從源點到各個頂點,以致從源點到匯點的有向路徑可能不止一條。這些路徑的長度也可能不一樣。完成不一樣路徑的活動所需的時間雖然不一樣,但只有各條路徑上全部活動都完成了,整個工程纔算完成。所以,完成整個工程所需的時間取決於從源點到匯點的最長路徑長度,即在這條路徑上全部活動的持續時間之和。這條路徑長度最長的路徑就叫作關鍵路徑(critical path)
求關鍵路徑的算法:
上一篇:樹(tree)
下一篇:排序(Sort)