ACM第四專題—圖算法總結
圖的基本知識
頂點:圖中的數據元素稱爲頂點.
有向圖:有方向的圖叫有向圖.
無向圖:沒有方向的圖叫無線圖.
完全圖:有n(n-1)/2條邊的無向圖稱爲完全圖.
有向完全圖:具有n(n-1)條弧的有向圖稱爲有向完全圖.
稀疏圖:有很少條邊或弧的圖稱爲稀疏圖,反之稱爲稠密圖.
權:與圖的邊或弧相關的數叫做權(weight).
1. Relaxation(鬆弛操作):
procedure relax(u,v,w:integer);//多數情況下不需要單獨寫成procedure。
begin
if dis[u]+w<dis[v] then
begin
dis[v]:=dis[u]+w;
pre[v]:=u;
end
end;
2. Dijkstra
1) 適用條件&範圍:
a) 單源最短路徑(從源點s到其它所有頂點v);
b) 有向圖&無向圖(無向圖可以看作(u,v),(v,u)同屬於邊集E的有向圖)
c) 所有邊權非負(任取(i,j)∈E都有Wij≥0);
2) 算法描述:
a) 初始化:dis[v]=maxint(v∈V,v≠s); dis[s]=0; pre[s]=s; S={s};
b) For i:=1 to n
1.取V-S中的一頂點u使得dis[u]=min{dis[v]|v∈V-S}
2.S=S+{u}
3.For V-S中每個頂點v do Relax(u,v,Wu,v)
c) 算法結束:dis[i]爲s到i的最短距離;pre[i]爲i的前驅節點
3) 算法優化:
使用二叉堆(Binary Heap)來實現每步的DeleteMin(ExtractMin,即算法步驟b中第1步)操作,算法複雜度從O(V^2)降到O((V+E)㏒V)。推薦對稀疏圖使用。
使用Fibonacci Heap(或其他Decrease操作O(1),DeleteMin操作O(logn)的數據結構)可以將複雜度降到O(E+V㏒V);如果邊權值均爲不大於C的正整數,則使用Radix Heap可以達到O(E+V㏒C)。但因爲它們編程複雜度太高,不推薦在信息學競賽中使用。
3. Floyd-Warshall
1) 適用範圍:
a) APSP(All Pairs Shortest Paths)
b) 稠密圖效果最佳
c) 邊權可正可負
2) 算法描述:
a) 初始化:dis[u,v]=w[u,v]
b) For k:=1 to n
For i:=1 to n
For j:=1 to n
If dis[i,j]>dis[i,k]+dis[k,j] Then
Dis[I,j]:=dis[I,k]+dis[k,j];
c) 算法結束:dis即爲所有點對的最短路徑矩陣
3) 算法小結:
此算法簡單有效,由於三重循環結構緊湊,對於稠密圖,效率要高於執行|V|次Dijkstra算法。時間複雜度O(n^3)。
考慮下列變形:如(I,j)∈E則dis[I,j]初始爲1,else初始爲0,這樣的Floyd算法最後的最短路徑矩陣即成爲一個判斷I,j是否有通路的矩陣。更簡單的,我們可以把dis設成boolean類型,則每次可以用“dis[I,j]:=dis[I,j]or(dis[I,k]and dis[k,j])”來代替算法描述中的藍色部分,可以更直觀地得到I,j的連通情況。
與Dijkstra算法類似地,算法中藍色的部分可以加上對Pre數組的更新,不再贅述。
4. Prim (Dijksta的推廣)
1) 適用範圍:
a) MST(Minimum Spanning Tree,最小生成樹)
b) 無向圖(有向圖的是最小樹形圖)
c) 多用於稠密圖
2) 算法描述:
a) 初始化:dis[v]=maxint(v∈V,v≠s); dis[s]=0; pre[s]=s; S={s};tot=0
b) For i:=1 to n
1.取頂點v∈V-S使得W(u,v)=min{W(u,v)|u∈S,v∈V-S,(u,v)∈E}
2.S=S+{v};tot=tot+W(u,v);輸出邊(u,v)
3.For V-S中每個頂點v do Relax(u,v,Wu,v)
c) 算法結束:tot爲MST的總權值
注意:這裏的Relax不同於求最短路徑時的鬆弛操作。它的代碼如下:
procedure relax(u,v,w:integer); //鬆弛操作
begin
if w<dis[v] then
begin
pre[v]:=u;
dis[v]:=w;
end;
end;
可以看到,雖然不同,卻也十分相似。
3) 算法優化:
使用二叉堆(Binary Heap)來實現每步的DeleteMin(ExtractMin)操作
算法複雜度從O(V^2)降到O((V+E)㏒V)。推薦對稀疏圖使用。
使用Fibonacci Heap可以將複雜度降到O(E+V㏒V),但因爲編程複雜度太高,不推薦在信息學競賽中使用。
(不要問我爲什麼和Dijkstra一樣……觀察我的prim和dijkstra程序,會發現基本上只有relax和輸出不一樣……)
5. Kruskal
1) 適用範圍:
a) MST(Minimum Spanning Tree,最小生成樹)
b) 無向圖(有向圖的是最小樹形圖)
c) 多用於稀疏圖
d) 邊已經按權值排好序給出
2) 算法描述:
基本思想:每次選不屬於同一連通分量(保證無圈)且邊權值最小的2個頂點,將邊加入MST,並將所在的2個連通分量合併,直到只剩一個連通分量
3) 算法實現:
a) 將邊按非降序排列(Quicksort,O(E㏒E))
b) While 合併次數少於|V|-1
i. 取一條邊(u,v)(因爲已經排序,所以必爲最小)
ii. If u,v不屬於同一連通分量 then
1) 合併u,v所在的連通分量
2) 輸出邊(u,v)
3) 合併次數增1;tot=tot+W(u,v)
c) 算法結束:tot爲MST的總權值
4) 分析總結:
檢查2個頂點是否在同一連通分量可以使用並查集實現(連通分量看作等價類)。
我們可以看到,算法主要耗時在將邊排序上。如果邊已經按照權值順序給出,那太棒了……
另外一種可以想到的實現方法爲:O(n)時間關於邊權建二叉小根堆;每次挑選符合條件的邊時使用堆的DelMin操作。這種方法比用Qsort預排序的方法稍微快一些,編程複雜度基本一樣。附程序。
另外,如果邊權有一定限制,即<=某常數c,則可以使用線性時間排序以獲得更好的時間效率。
頂點:圖中的數據元素稱爲頂點.
有向圖:有方向的圖叫有向圖.
無向圖:沒有方向的圖叫無線圖.
完全圖:有n(n-1)/2條邊的無向圖稱爲完全圖.
有向完全圖:具有n(n-1)條弧的有向圖稱爲有向完全圖.
稀疏圖:有很少條邊或弧的圖稱爲稀疏圖,反之稱爲稠密圖.
權:與圖的邊或弧相關的數叫做權(weight).
1. Relaxation(鬆弛操作):
procedure relax(u,v,w:integer);//多數情況下不需要單獨寫成procedure。
begin
if dis[u]+w<dis[v] then
begin
dis[v]:=dis[u]+w;
pre[v]:=u;
end
end;
2. Dijkstra
1) 適用條件&範圍:
a) 單源最短路徑(從源點s到其它所有頂點v);
b) 有向圖&無向圖(無向圖可以看作(u,v),(v,u)同屬於邊集E的有向圖)
c) 所有邊權非負(任取(i,j)∈E都有Wij≥0);
2) 算法描述:
a) 初始化:dis[v]=maxint(v∈V,v≠s); dis[s]=0; pre[s]=s; S={s};
b) For i:=1 to n
1.取V-S中的一頂點u使得dis[u]=min{dis[v]|v∈V-S}
2.S=S+{u}
3.For V-S中每個頂點v do Relax(u,v,Wu,v)
c) 算法結束:dis[i]爲s到i的最短距離;pre[i]爲i的前驅節點
3) 算法優化:
使用二叉堆(Binary Heap)來實現每步的DeleteMin(ExtractMin,即算法步驟b中第1步)操作,算法複雜度從O(V^2)降到O((V+E)㏒V)。推薦對稀疏圖使用。
使用Fibonacci Heap(或其他Decrease操作O(1),DeleteMin操作O(logn)的數據結構)可以將複雜度降到O(E+V㏒V);如果邊權值均爲不大於C的正整數,則使用Radix Heap可以達到O(E+V㏒C)。但因爲它們編程複雜度太高,不推薦在信息學競賽中使用。
3. Floyd-Warshall
1) 適用範圍:
a) APSP(All Pairs Shortest Paths)
b) 稠密圖效果最佳
c) 邊權可正可負
2) 算法描述:
a) 初始化:dis[u,v]=w[u,v]
b) For k:=1 to n
For i:=1 to n
For j:=1 to n
If dis[i,j]>dis[i,k]+dis[k,j] Then
Dis[I,j]:=dis[I,k]+dis[k,j];
c) 算法結束:dis即爲所有點對的最短路徑矩陣
3) 算法小結:
此算法簡單有效,由於三重循環結構緊湊,對於稠密圖,效率要高於執行|V|次Dijkstra算法。時間複雜度O(n^3)。
考慮下列變形:如(I,j)∈E則dis[I,j]初始爲1,else初始爲0,這樣的Floyd算法最後的最短路徑矩陣即成爲一個判斷I,j是否有通路的矩陣。更簡單的,我們可以把dis設成boolean類型,則每次可以用“dis[I,j]:=dis[I,j]or(dis[I,k]and dis[k,j])”來代替算法描述中的藍色部分,可以更直觀地得到I,j的連通情況。
與Dijkstra算法類似地,算法中藍色的部分可以加上對Pre數組的更新,不再贅述。
4. Prim (Dijksta的推廣)
1) 適用範圍:
a) MST(Minimum Spanning Tree,最小生成樹)
b) 無向圖(有向圖的是最小樹形圖)
c) 多用於稠密圖
2) 算法描述:
a) 初始化:dis[v]=maxint(v∈V,v≠s); dis[s]=0; pre[s]=s; S={s};tot=0
b) For i:=1 to n
1.取頂點v∈V-S使得W(u,v)=min{W(u,v)|u∈S,v∈V-S,(u,v)∈E}
2.S=S+{v};tot=tot+W(u,v);輸出邊(u,v)
3.For V-S中每個頂點v do Relax(u,v,Wu,v)
c) 算法結束:tot爲MST的總權值
注意:這裏的Relax不同於求最短路徑時的鬆弛操作。它的代碼如下:
procedure relax(u,v,w:integer); //鬆弛操作
begin
if w<dis[v] then
begin
pre[v]:=u;
dis[v]:=w;
end;
end;
可以看到,雖然不同,卻也十分相似。
3) 算法優化:
使用二叉堆(Binary Heap)來實現每步的DeleteMin(ExtractMin)操作
算法複雜度從O(V^2)降到O((V+E)㏒V)。推薦對稀疏圖使用。
使用Fibonacci Heap可以將複雜度降到O(E+V㏒V),但因爲編程複雜度太高,不推薦在信息學競賽中使用。
(不要問我爲什麼和Dijkstra一樣……觀察我的prim和dijkstra程序,會發現基本上只有relax和輸出不一樣……)
5. Kruskal
1) 適用範圍:
a) MST(Minimum Spanning Tree,最小生成樹)
b) 無向圖(有向圖的是最小樹形圖)
c) 多用於稀疏圖
d) 邊已經按權值排好序給出
2) 算法描述:
基本思想:每次選不屬於同一連通分量(保證無圈)且邊權值最小的2個頂點,將邊加入MST,並將所在的2個連通分量合併,直到只剩一個連通分量
3) 算法實現:
a) 將邊按非降序排列(Quicksort,O(E㏒E))
b) While 合併次數少於|V|-1
i. 取一條邊(u,v)(因爲已經排序,所以必爲最小)
ii. If u,v不屬於同一連通分量 then
1) 合併u,v所在的連通分量
2) 輸出邊(u,v)
3) 合併次數增1;tot=tot+W(u,v)
c) 算法結束:tot爲MST的總權值
4) 分析總結:
檢查2個頂點是否在同一連通分量可以使用並查集實現(連通分量看作等價類)。
我們可以看到,算法主要耗時在將邊排序上。如果邊已經按照權值順序給出,那太棒了……
另外一種可以想到的實現方法爲:O(n)時間關於邊權建二叉小根堆;每次挑選符合條件的邊時使用堆的DelMin操作。這種方法比用Qsort預排序的方法稍微快一些,編程複雜度基本一樣。附程序。
另外,如果邊權有一定限制,即<=某常數c,則可以使用線性時間排序以獲得更好的時間效率。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.