資料結構圖
知識要點:
圖的各種基本概念和各種儲存方式;(鄰接矩陣、鄰接連結串列)
圖的兩種搜尋方法和圖連的連通性;(深度優先搜尋、廣度優先搜尋)
兩種最小生成樹的生成方法;(普里姆演算法(加點法),克魯斯卡爾演算法(加邊法))
各種求最短路徑的方法;(迪傑斯特拉演算法,弗洛伊德演算法)
用頂點表示活動和用邊表示活動的兩種網路結構特點和相關操作的實現演算法。(拓撲排序、AOE閘道器鍵路徑)
1、圖的基本概念
定義:圖G由兩部分組成,頂點集合V和邊集合E,任何一個圖都不能為空(即V不能為空E可以為空);
無向圖:若E是無向邊的集合,此圖G為無向圖;(邊無正向和反向)
有向圖:若E是有向邊的集合,此圖G為有向圖;(邊存在正向和反向)
簡單圖:1)不存在重複的邊(無平行邊) 2)不存在頂點到自身的邊(無環) 注多重圖的定義與之相反;
子圖:若G1圖中的點和邊在G2圖中都有則稱G1為G2的子圖;(反過來未必成立)
無向完全圖和有向完全圖:(和圖中所有的頂點都產生關聯的圖)
無向完全圖邊的個數為n(n-1)/2;有向完全圖邊的個數為n(n-1);
稠密圖和稀疏圖:根據邊的數目來定義,邊多->稠密圖、邊少->稀疏圖;
權和網:邊上給予一定的值稱為該邊的權值,帶有權值的圖稱之為網;
度、入度、出度:
度是指與某一結點相關聯的邊的數目,稱之為該結點的度;入度和出度對應的是有向圖中,入度是指以某一結點為頭的
邊的數目,出度是指以某一結點為尾的邊的數目;
性質:1)圖中所有頂點的度數總和等於邊數目的2倍;//一條邊提供2度
2)對於有向圖中入度等於出度等於邊的數目;
連通、連通圖、連通分量:
1)圖中任意兩個結點之間都存在路徑,則此圖是連通的,該圖稱為連通圖;
2)連通分量:圖中的極大連通子圖;
總結:存有路徑即連通、全部結點都有路徑為連通圖、極大連通子圖為連通分量;
強連通圖和強連通分量:(瞭解)
針對有向圖的連通性,圖中v到w和w到v之間都存在有向路徑,則稱此兩結點是強連通的,圖中任意頂點都是強連通的
則此圖稱為強連通圖,極大強連通子圖稱之為強連通分量;
生成樹與生成森林:生成樹是指使圖中全部頂點相互連通的極小連通子圖
子圖要有n-1條邊;如果圖本身就是非連通的,則由每一個連通分量生成的各自的生成樹稱之為生成森林;
路徑:是指在連通圖中從起點到終點途中所經過的有限頂點序列集合;
路徑長度:是指從起點到終點所經歷的邊的數目;
簡單迴路或者環:起點和終點相同的路徑稱之為環或者回路;
簡單路徑:路徑中的頂點不重複出現;簡單迴路或簡單環:除了起點和終點,其餘各個頂點都不重複;
總結:極大連通子圖對應的是連通分量,極小連通子圖對應的是生成樹;
☆☆☆重點 圖的兩種存取方式(鄰接矩陣和鄰接連結串列)
鄰接矩陣:(採用二維陣列存取即可)
對於無向圖中:陣列內元素A[i][j]為1表示存在由 i 到 j 的邊,如果為0表示不存在由 i 到 j 的邊;
性質: 1)無向圖的鄰接矩陣是對稱矩陣;
2)無向圖中非零元素個數為該圖邊的數目;
3)圖中頂點 i 對應的度的大小為橫向(縱向)非零元素的個數;
對於有向圖(網)中:陣列內元素A[i][j]為k(k!=∞)表示存在由 i 到 j 的邊,且該邊權值為k;反之不存在由 i 到 j 的邊;
性質: 1)有向圖未必是對稱矩陣;
2)有向圖中非零且非無窮元素個數為該圖邊的數目;
3)圖中頂點 i 對應的出度為橫向非零且非無窮元素個數;圖中頂點 i 對應的入度為縱向非零且非無窮元素個數;
生成鄰接矩陣的時間複雜度為O(n^2);對於稠密圖可以採用鄰接矩陣存取;
鄰接連結串列:(由頂點表和邊表組成)
頂點表存有頂點資訊,邊表存有該頂點直接相關聯的頂點;
性質: 1)邊表的結點數目,即為該圖中的邊的條數;
2)對於有向圖中獲取某一頂點的出度只需計算與之相關聯的邊表的結點個數即可,時間複雜度為O(n);
但要想知道圖中某一頂點的入度需要訪問整個鄰接連結串列,時間複雜度為O(n+e);
3)鄰接連結串列的生成與演算法有關,生成的鄰接表並不唯一;
生成鄰接連結串列的時間複雜度為O(n+e);對於稀疏圖常採用鄰接連結串列;
其他儲存方式:十字連結串列(有向圖),鄰接多重表(無向圖);
對於圖的鄰接矩陣表示其表示是唯一的,而對於鄰接連結串列,由於演算法的不同其表示方法不唯一;
☆☆☆圖的兩種遍歷方式:(深度遍歷、廣度遍歷)
深度優先遍歷過程:(前序遍歷)
1)從某一個結點出發,訪問此結點並將其標誌為已訪問;
2)找出剛訪問頂點的第一個未被訪問的鄰接點,訪問此鄰接點、重複此步驟直至訪問過的頂點不存在未被訪問的鄰接點為止;
3)回溯至前一個訪問過的鄰接點位置,繼續訪問未被訪問的鄰接點;
4)重複步驟2和3直到圖中所有頂點均被訪問為止;
由深度優先搜尋構成的樹稱之為深度優先生成樹;
廣度優先遍歷過程:(層次遍歷)
1)從某一個結點出發,訪問此結點並將其標誌為已訪問;
2)訪問與剛訪問的頂點相關聯的所有鄰接點,並將這些點加入一個佇列中去,並將這些點標誌為已訪問;
3)(取隊頭頂點)分別從這些點出發,在訪問與這些頂點相關聯的且未被訪問的鄰接點,
將這些點加入到佇列尾部,並將這些點標誌為已訪問;
4)重複2和3步驟直至佇列為空;
由廣度優先搜尋構成的樹稱之為廣度優先生成樹;
無向圖的遍歷過程中如果從一個頂點出發一次遍歷就可以訪問全部結點則此圖是連通的,反之就是非連通的;呼叫遍歷方法的
次數即為連通分量的個數;
☆☆☆最小生成樹的兩種演算法(普里姆演算法、克魯思卡爾演算法)
最小生成樹的定義:連通整個圖代價之和最小的那一顆生成樹(極小連通子圖)
普里姆演算法(Prim):以點為主
思想:構建兩個點集合A與B,其中A為已確定為生成樹的部分,B為未確定待新增的部分;
找出有集合B內某一點,使得此點到集合A的路徑長度最小,將集合B中的這一點加入到集合A中,
重複此步驟直到集合B中不存在結點為止;對於含有n個結點的圖只需重複n-1次即可;
克魯思卡爾演算法(Kruskal):以邊為主
思想:將圖中所有的邊從小到大依次排序,選出其中邊最小的將關聯的結點置於一個連通分量中。
之後在不同的連通分量之間選取最小邊,重置連通分量,直到所有的結點都位於一個連通分量時為止;
☆☆☆最短路徑的兩種演算法(迪傑斯特拉演算法,弗洛伊德演算法)
迪傑斯特拉演算法思想:最短路徑要麼直接到達,要麼經過最短中轉結點到達;
來看一道例題:(從1到其他結點的最短路徑)紅色點為最短路徑長度;
終點 | 1 | 2 | 3 | 4 | 5 |
1 | ture | true | true | true | true |
2 | 10 | true | true | true | true |
3 | 無窮 | 60 | 50 | true | true |
4 | 30 | 30 | true | true | true |
5 | 100 | 100 | 90 | 60 | true |
S | 02 | 024 | 0243 | 02435 | 02435 |
第一次確定1到2為最短路徑,路徑長度為20;
第二次確定1到4為最短路徑,路徑長度為30;
第三次確定1到4到3為最短路徑,路徑長度為50;
第四次確定1到4到3到5為最短路徑,路徑長度為60;//由此過程得出最短路徑要麼直接到達,要麼經歷最短中間路徑到達;
演算法的時間複雜度:求解一個結點到其他結點O(n^2),求解全部結點O(n^3)
弗洛伊德演算法:(採用鄰接矩陣實現)
思想:允許通過中轉結點訪問不斷地修改整個矩陣的值當經歷n-1次迴圈後所得到的矩陣就是各個點相互到達的最短路徑矩陣;
初始化:Picture[ i ][ j ];值表示從結點 i 到結點 j 的路徑;不經過任何結點直接相互關聯的鄰接矩陣;
Picture[ i ][ j ]=Picture[ i ][ k ]+Picture[ k ][ j ]; //經歷中間轉折點 k 從 i 到 j 後的結點路徑;
for(int k=0;k<n;k++){ //弗洛伊德演算法
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(Pic[i][j]>Pic[i][k]+Pic[k][j]){
Pic[i][j]=Pic[i][k]+Pic[k][j];
//依次經歷一個、兩個、三個.....結點逐步修改矩陣的值
//從i到j的路徑,經過k結點到達
}
}
}
時間複雜度O(n^3),注意此演算法不能解決“負權迴路”的圖的最短路徑問題
☆☆☆重點 拓撲排序:(判斷一個AOV網中是否有環存在)
步驟:1)在有向圖中找出入度為0的所有結點;(找無前驅結點)
2)刪除該結點以及與之相互關聯的邊;(刪除無前驅結點以及邊)
3)重複上述兩步驟直到圖中不存在入度為0的結點;
總結:存在拓撲排序圖中無環,而且由於演算法的不同得到的拓撲排序序列也不相同;
☆☆☆重點 關鍵路徑(要用到拓撲排序,AOE網)
AOE網:邊表示活動,頂點表示事件的圖,邊上的權值通常表示該活動的持續時間;
性質: 1)在AOE網中有且只有一個入度為0的點,該點稱為源點,有且只有一個出度為0的點,該點稱為匯點;
2)從源點到匯點的最長路徑稱為關鍵路徑,一個AOE網中的關鍵路徑可能有多條,位於關鍵路徑上的活動
稱為關鍵活動,關鍵活動會影響工程的提前或拖延;
經常考察的四個定義:
1)事件vi的最早開始時間;
進入事件vi的每一個活動都完成事件vi才能進行;
定義ve(0)=0;ve(i)=Max{ve(k),w(k,i)} //k點與i點直接相連;
事件vi的所有前驅結點的最早開始時間+活動<ak,ai>的權值的最大值;
2)事件vi的最遲開始時間;
要求:vi的發生不會延誤vi後面的事件發生的最遲時間;
為了不拖延工期,vi的最遲發生時間不得遲與後繼事件vk的最遲發生時間減去活動<vi,vk>的權值;
定義vl(n-1)=ve(n-1);//活動最後總時間
vl(i)=Min{vl(k)-w(i,k)} //i與k直接相連
活動vi進行完,剛好能進行下面的活動,使得最後完成的工作不受影響;
3)活動ai=<vj,vk>的最早開始時間;
當事件vj發生了與活動相關聯的邊既可以進行;
活動a的最早發生時間與事件vj的最早開始時間相同,即e(ai)=ve(j);
4)活動ai=<vj,vk>的最遲開始時間;
要求:活動ai的開始時間保證不延誤事件vk的最遲發生時間;
活動ai=<vi,vk>不得延誤vk的最遲發生時間,所以活動的最遲開始時間等於事件vk的最遲開始時間減去活動<vi,vk>的權值
即:l(i)=vl(k)-w{j,k};
總結:對於最早開始時間都採用順推的方式進行,對於最遲開始時間均採用逆推的方式進行;
由下圖分析以上四個時間問題:
圖中的關鍵路徑為A->B->E->H->I或者A->B->E->G->I;關鍵路徑不唯一
各個事件最早開始時間(採用順推的方式進行即可):
ve(A)=0(源點最早發生時間為0);ve(B)=6(事件A的最早發生時間+活動AB的權值);
ve(C)=4(事件A的最早發生時間+活動AC的權值);ve(D)=5(事件A的最早發生時間+活動AD的權值);
ve(E)=7(事件B最早發生時間+活動BE的權值為7,事件C的最早發生時間+活動CE的權值為5,取兩者的最大值);
ve(F)=7(事件D最早發生時間+活動DF的權值);
ve(G)=16(事件E最早發生時間+活動EG的權值);
ve(H)=14(事件E最早發生時間+活動EH的權值);
ve(I)=18(事件G最早發生時間+活動GI的權值為18,事件H最早發生時間+活動HI的權值為18,取兩者的最大值);
各個活動的最早開始時間(採用順推的方式進行即可):
e(AB)=0;e(AC)=0;e(AD)=0;(等於事件A的最早開始時間);
e(BE)=6;(等於事件B的最早開始時間)e(CE)=4;(等於事件C的最早開始時間);
e(DF)=5;(等於事件D的最早開始時間)e(EG)=7;(等於事件E的最早開始時間);
e(EH)=7;(等於事件E的最早開始時間)e(FH)=7;(等於事件F的最早開始時間);
e(GI)=16;(等於事件G的最早開始時間)e(HI)=14;(等於事件H的最早開始時間);
各個事件的最遲開始時間(採用逆推的方式進行即可):
vl(I)=18;(等於關鍵活動的最長工期);
vl(H)=14;(等於事件I的最遲發生時間減去活動HI的權值);
vl(G)=16;(等於事件I的最遲發生時間減去活動GI的權值);
vl(F)=10;(事件H的最遲發生時間減去活動EH的權值為);
vl(E)=7;(事件H的最遲發生時間減去活動EH的權值為7,事件G的最遲發生時間減去活動EG的權值為7,兩者取最小值);
vl(D)=8;(事件F的最遲發生時間減去活動DF的權值);
vl(C)=6;(事件E的最遲發生時間減去活動CE的權值);
vl(B)=6;(事件E的最遲發生時間減去活動BE的權值);
vl(A)=0;(事件B的最遲發生時間減去活動AB的權值為0,事件C的最遲發生時間減去活動AC的權值為2,
事件D的最遲發生時間減去活動AD的權值為1,三者取最小值);
各個活動的最遲開始時間(採用逆推的方式進行即可):
l(GI)=16;(事件I的最晚發生時間減去活動GI的持續時間)
I(HI)=14;(事件I的最晚發生時間減去活動HI的持續時間)
l(FH)=10;(事件H的最晚發生時間減去活動FH的持續時間)
l(EG)=7;(事件G的最晚發生時間減去活動EG的持續時間)
l(EH)=7;(事件H的最晚發生時間減去活動EH的持續時間)
I(DF)=8;(事件F的最晚發生時間減去活動DF的持續時間)
I(BE)=6;(事件E的最晚發生時間減去活動BE的持續時間)
I(CE)=6;(事件E的最晚發生時間減去活動CE的持續時間)
I(AD)=3;(事件D的最晚發生時間減去活動AD的持續時間)
I(AC)=2;(事件C的最晚發生時間減去活動AC的持續時間)
I(AB)=0;(事件B的最晚發生時間減去活動AB的持續時間)
對於活動來說:最早和最遲的開始時間相同,此活動為關鍵活動;
常見時間複雜度問題:(鄰接矩陣、鄰接連結串列)
求邊的數目:鄰接矩陣O(n^2),鄰接連結串列對於有向圖O(n+e),對於無向圖O(n+2e);
建立圖的複雜度:鄰接矩陣O(n^2),鄰接連結串列O(n+e);
求頂點的度:鄰接矩陣的出入度O(n);鄰接連結串列無向圖最壞O(n),有向圖中出度O(n),入度時間複雜度O(n+e)