❶ 迪傑特斯拉演算法和普里姆演算法做法是不是類似的
不是,迪傑斯特拉演算法是算一點到其他所有點的最短路徑
普利姆演算法是算最小生成樹的。
普利姆演算法是在已加入的集合上,長新的邊,挑距離這個集合最短的(就是無論連到哪一點,只要連到這個集合上,距離最短)
地傑斯特拉每一步是挑距離欲求的點最短的點加入。
用自然語言描述很難說清,按照例子試一下吧。
❷ sh實現最小生成樹和最短路徑的演算法
圖的最小生成樹與最短路徑的演算法
一、圖的生成樹與最小生成樹
在一個連通圖G中,如果取它的全部頂點和一部分邊構成一個子圖G』,即:
若邊集E(G』)中的邊既將圖中的所有頂點連通又不形成迴路,則稱子圖G』是原圖G的一棵生成樹。
最小生成樹:給圖中每個邊賦一權值,所有生成樹中所選擇邊的權值之和最小的生成樹,稱之為最小代價生成樹,即是最小生成樹。
1、普里姆演算法
1.1演算法描述
假設G=(V, E)是一個具有n個頂點的連通網,T=(U, TE)是G的最小生成樹,其中U是T的頂點集,TE是T的邊集,U和TE的初值均為空集。演算法開始時,首先從V中任取一個頂點(假定取v1),將它並入U中,此時U={v1},然後只要U是V的真子集(即),就從那些其一個端點已在T中,另一個端點仍在T外的所有邊中,找一條最短(即權值最小)邊,假定為(vi, vj),其中,並把該邊(vi, vj)和頂點vj分別並入T的邊集TE和頂點集U,如此進行下去,每次往生成樹里並入一個頂點和一條邊,直到(n-1)次後就把所有n個頂點都並入到生成樹T的頂點集中,此時U=V,TE中含有(n-1)條邊,T就是最後得到的最小生成樹。 1.2關鍵問題
普里姆演算法的關鍵之處是:每次如何從生成樹T中到T外的所有邊中,找出一條最短邊。例如,在第k次前,生成樹T中已有k個頂點和(k-1)條邊,此時T中到T外的所有邊數為k(n-k),當然它包括兩頂點間沒有直接邊相連,其權值被看作為「無窮大」的邊在內,從如此多的邊中查找最短邊,其時間復雜性為O(k(n-k)),顯然是很費時的。是否有一種好的方法能夠降低查找最短邊的時間復雜性呢? 1.3 解決方法
方法是:假定在進行第k次前已經保留著從T中到T外每一頂點(共(n-k)個頂點)的各一條最短邊,進行第k次時,首先從這(n-k)條最短邊中,找出一條最最短的邊(它就是從T中到T外的所有邊中的最短邊),假設為(vi, vj),此步需進行(n-k)次比較;然後把邊(vi, vj)和頂點vj分別並入T中的邊集TE和頂點集U中,此時T外只有n-(k+1)個頂點,對於其中的每個頂點vt,若(vj, vt)邊上的權值小於已保留的從原T中到vt的最短邊的權值,則用(v, vt)修改之,使從T中到T外頂點vt的最短邊為(vj, vt),否則原有最短邊保持不變,這樣,就把第k次後從T中到T外每一頂點vt的各一條最短邊都保留下來了。為進行第(k+1)次運算做好了准備,此步需進行(n-k-1)次比較。所以,利用此方法求第k次的最短邊共需比較2(n-k)-1次,即時間復雜性為O(n-k)。
1.4 prim演算法:
設一個輔助數組closedge,以記錄從U到V—U具有最小代價的邊。數組中的每個元素closedge[v]是記錄類型,包含兩個域: closedge[v].lowcast=Min{cost(u,v)|u∈U}; closedge[v].vex存儲該邊依附的在U中的頂點。
proc mintree_prim(gn:adjmatrix;u0:integer);
begin
for v:=1 to n do
if v<>u0 then
with closedage[v] do [vex:=u0; lowcast:=gn[u0,v];]
closedge[u0].lowcast:=0;{並入U集合}
for i:=1 to n-1 do
begin
v:=min(closedge);{尋找代價最小的邊}
write(closedge[v].vex,v); closedge[v].lowcast:=0;{並入U集合}
for k:=1 to n do
if gn[v,k]<closedge[k].lowcast then
begin closedge[k].lowcast:=gn[v,k]; closedge[k].vex:=v; end;
end;
end; 練習1:prim演算法實現
【問題描述】從文件中讀入連通帶權圖的信息,按prim演算法求出該圖的最小生成樹,以V1作為初始結點。
【輸入文件】第一行兩個整數m和n,分別表示圖的結點數和圖中的邊數。以下n行表示n條邊:每一行三個數x、y和k,k表示x與y之間邊的權值。
【輸出文件】共m行,第一行:最小生成樹的權;以下m-1行表示選取的邊,邊的第1個結點小於第2個結點,並按結點由小到大輸出。
【示例】輸入:5 7 輸出:45
1 2 17 1 4
2 3 30 1 5
1 4 5 2 4
2 4 10 3 5
3 4 24
3 5 7
1 5 23
練習2: Eddy painting
Eddy begins to like painting pictures recently ,he is sure of himself to become a painter.Every day Eddy draws pictures in his small room, and he usually puts out his newest pictures to let his friends appreciate. but the result it can be imagined, the friends are not interested in his picture.Eddy feels very puzze,in order to change all friends 's view to his technical of painting pictures ,so Eddy creates a problem for the his friends of you.
Problem descriptions as follows: Given you some coordinates pionts on a drawing paper, every point links with the ink with the straight line, causes all points finally to link in the same place. How many distants does your ty discover the shortest length which the ink draws?
Input:
The first line contains 0 < n <= 100, the number of point. For each point, a line follows; each following line contains two real numbers indicating the (x,y) coordinates of the point.
Input contains multiple test cases. Process to the end of file.
Output:
Your program prints a single real number to two decimal places: the minimum total length of ink lines that can connect all the points.
Sample Input:
3
1.0 1.0
2.0 2.0
2.0 4.0
Sample Output:
3.41
2、克魯斯卡爾演算法
2.1 演算法描述
假設G=(V,E)是一個具有n個頂點的連通網,T=(U,TE)是G的最小生成樹,U的初值等於V,即包含有G中的全部頂點,TE的初值為空。此演算法的基本思想是,將圖G中的邊按權值從小到大的順序依次選取,若選取的邊使生成樹T不形成迴路,則把它並入TE中,保留作為T的一條邊,若選取的邊使生成樹T形成迴路,則將其舍棄,如此進行下去,直到TE中包含有n-1條邊為止。此時的T即為最小生成樹。
2.2 關鍵問題
克魯斯卡爾演算法的關鍵之處是:如何判斷欲加入的一條邊是否與生成樹中已選取的邊形成迴路。這可將各頂點劃分為所屬集合的方法來解決,每個集合中的頂點表示一個無迴路的連通分量。演算法開始時,由於生成樹的頂點集等於圖G的頂點集,邊集為空,所以n個頂點分屬於n個集合。每個集合中只有一個頂點,表明頂點之問互不連通。
2.3 Kruskal演算法:
proc mintree_krusk(gn:adjmatrix);
begin
for i:=1 to n do
un[i]:=i;
for i:=1 to n-1 do
begin
minedge(a,b);
write(a,b,gn[a,b]);
k:=un[b];
for i:=1 to n do {兩個連通分量合並}
if un[i]=k then un[i]:=un[a];
end;
end;
2.4 注意:
proc minedge(var a:integer;var b:integer);用於在剩下的邊中選取不再同一連通分量上的最小代價的邊,邊的結點分別為a和b。
為了實現該過程,可以將圖中的邊生成一邊結點(包含兩個頂點和代價)數組,由小到大排序,然後通過排序後的數組進行處理;
un數組:用來記載隨著邊的加入,各頂點屬於哪個連通分量。
練習3:Kruskal演算法實現
【問題描述】從文件中讀入連通帶權圖的信息,按Kruskal演算法求出該圖的最小生成樹,以V1作為初始結點。
【輸入文件】第一行兩個整數m和n,分別表示圖的結點數和圖中的邊數。以下n行表示n條邊:每一行三個數x、y和k,k表示x與y之間邊的權值。
【輸出文件】共m行,第一行:最小生成樹的權;以下m-1行表示選取的邊,按選取邊的權值由小到大輸出。
【示例】輸入:5 7 輸出:45
1 2 17 1 4
2 3 30 3 5
1 4 5 2 4
2 4 10 1 5
3 4 24
3 5 7
1 5 23
練習4:判斷最小生成樹是否唯一
Given a connected undirected graph, tell if its minimum spanning tree is unique.
Definition 1 (Spanning Tree): Consider a connected, undirected graph G = (V, E). A spanning tree of G is a subgraph of G, say T = (V', E'), with the following properties:
1. V' = V.
2. T is connected and acyclic.
Definition 2 (Minimum Spanning Tree): Consider an edge-weighted, connected, undirected graph G = (V, E). The minimum spanning tree T = (V, E') of G is the spanning tree that has the smallest total cost. The total cost of T means the sum of the weights on all the edges in E'.
Input
The first line contains a single integer t (1 <= t <= 20), the number of test cases. Each case represents a graph. It begins with a line containing two integers n and m (1 <= n <= 100), the number of nodes and edges. Each of the following m lines contains a triple (xi, yi, wi), indicating that xi and yi are connected by an edge with weight = wi. For any two nodes, there is at most one edge connecting them.
Output
For each input, if the MST is unique, print the total cost of it, or otherwise print the string 'Not Unique!'.
Sample Input
2
3 3
1 2 1
2 3 2
3 1 3
4 4
1 2 2
2 3 2
3 4 2
4 1 2
Sample Output
3
Not Unique!
二、最短路徑
【問題描述】由於從一頂點到另一頂點可能存在著多條路徑。每條路徑上所經過的邊數可能不同,即路徑長度不同,我們把路徑長度最短(即經過的邊數最少)的那條路徑叫做最短路徑,其路徑長度叫做最短路徑長度或最短距離。求圖中一頂點vi到其餘各頂點的最短路徑和最短距離比較容易,只要從該頂點vi,出發對圖進行一次廣度優先搜索遍歷,在遍歷時記下每個結點的層次即可。
若圖是帶權圖(假定權值非負)從源點vi到終點vj的每條路徑上的權(它等於該路徑上所經邊上的權值之和,稱為該路徑的帶權路徑長度)可能不同,我們把權值最小的那條路徑也稱做最短路徑,其權值也稱作最短路徑長度或最短距離。
實際上,這兩類最短路徑問題可合並為一類,這只要把第一類的每條邊的權都設為1就歸屬於第二類了,所以在以後的討論中,若不特別指明,均是指第二類的最短路徑問題。
求圖的最短路徑問題包括兩個子問題:一是求圖中一頂點到其餘各頂點的最短路徑,二是求圖中每對頂點之間的最短路徑。下面分別進行討論。
始點 終點 最短路徑 路徑長度
v0 v1 No path
v2 (v0,v2) 10
v3 (v0,v4,v3) 50
v4 (v0,v4) 30
v5 (v0,v4,v3,v5) 60
始點 終點 最短路徑 路徑長度
v1 V2 (v1,v2) 10
V3 (v1,v2,v3) 27
V4 (v1,v5,v4) 20
v5 (v1,v5) 7
1、從一頂點到其餘各頂點的最短路徑
1.1 描述
迪傑斯特拉(Dijkstra)於1959年提出了解決此問題的一般演算法,具體做法是按照從源點到其餘每一頂點的最短路徑長度的升序依次求出從源點到各頂點的最短路徑及長度,每次求出從源點vi到一個終點vj的最短路徑及長度後,都要以vj作為新考慮的中間點,用vi到vj的最短路徑和最短路徑長度對vi到其它尚未求出最短路徑的那些終點的當前路徑及長度作必要的修改,使之成為當前新的最短路徑和最短路徑長度,當進行n-2次後演算法結束。
1.2 Dijkstra演算法:
首先,引進一個輔助向量dist,dist[i]表示當前所找到的從始點V到每個終點Vi的最短路徑長度。其初態為:若<v,vi>存在,則dist[i]為其上的權值,否則為最大值(計算機能表示)。
演算法:(1)用鄰接矩陣cost表示帶權有向圖。S表示已找到的從v出發的最短路徑的終點的集合,初態為空。dist向量的初值為:dist[v,i]=cost[v,i];
(2)選擇vj,使得:dist[j]=Min{dist[i]|vi∈V-S};vj就是當前求得從v出發的最短路徑的終點。
S=S+{j};
(3)修改從v出發到集合V-S上任意頂點vk可達的最短路徑長度。
if dist[j]+cost[j,k]<dist[k] then dist[k]:=dist[j]+cost[j,k];
(4)重復(2)(3)共n-1次。
代碼:proc short_dij;
begin
for i:=1 to n do
begin
dist[i]:=cost[v0,i];
if dist[i]<max then path[i]:=v0 else path[i]:=-1; end;
flag[I]:=true;
for k:=1 to n-1 do
begin
wm:=max; j:=v0;
for i:=1 to n do
if not(flag[i]) and (dist[i]<wm) then begin j:=i; m:=dist[i]; end;
flag[j]:=true; for i:=1 to n do if not(flag[i]) and (dist[j]+cost[j,i]<dist[i]) then
begin dist[i]:=dist[j]+cost[j,i]; path[i]:=j; end;
end;
end; 其中:cost:鄰接矩陣;
path[i]:存儲從v0到頂點i的最短路徑;是以集合作為數組元素;
dist[i]:存儲相應路徑長度;
flag[i]:表示已處理的頂點。
練習5:Dijkstra演算法練習
【問題描述】從文件中讀入帶權圖的信息,按Dijkstra演算法根據給定源點求出從源點法到該圖中其餘頂點的最短路徑。
【輸入文件】第一行:一個整數L:L=0表示無向圖,L=1表示有向圖;第二行三個整數m、n和k,分別表示圖的結點數、圖中的邊數以及源點。以下n行表示n條邊:每一行三個數x、y和z,z表示x與y之間邊的權值。
【輸出文件】共m-1行,每一行的數據包括:頂點: 最短路徑:路徑,如果不存在路徑,數據為:頂點:No path。
【示例】輸入:1 輸出:2:No path
6 8 1 3:10:1 3
1 3 10 4:50:1 5 4
1 5 30 5:30:1 5
1 6 100 6:60:1 5 4 6
2 3 5
3 4 50
4 6 10
5 4 20
5 6 60
練習6:路由選擇問題
【問題描述】
X城有一個含有N個節點的通信網路,在通信中,我們往往關心信息從一個節點I傳輸到節點J的最短路徑。遺憾的是,由於種種原因,線路中總有一些節點會出故障,因此在傳輸中要避開故障節點。
任務一:在己知故障節點的情況下,求避開這些故障節點,從節點I到節點J的最短路徑S0。
任務二:在不考慮故障節點的情況下,求從節點I到節點J的最短路徑S1、第二最短路徑S2。
【輸入文件】
第1行: N I J (節點個數 起始節點 目標節點)
第2—N+1行: Sk1 Sk2…SkN (節點K到節點J的距離為SkJ K=1,2,……,N)
最後一行: P T1 T2……Tp (故障節點的個數及編號)
【輸出文件】
S0 S1 S2 (S1<=S2 從節點I到節點J至少有兩條不同路徑)
【輸入輸出樣例】
route.in
5 1 5
0 10 5 0 0
10 0 0 6 20
5 0 0 30 35
0 6 30 0 6
0 20 35 6 0
1 2
route.out
40 22 30
2、每對頂點之間的最短路徑
求圖中每對頂點之間的最短路徑是指把圖中任意兩個頂點vi和vj(i≠j)之間的最短路徑都計算出來。解決此問題有兩種方法:一是分別以圖中的每個頂點為源點共調用n次迪傑斯特拉演算法,此方法的時間復雜性為O(n3);二是採用下面介紹的弗洛伊德(Floyed)演算法,此演算法的時間復雜性仍為O(n3),但比較簡單。 弗洛伊德演算法實際上是一個動態規劃的演算法。從圖的鄰接矩陣開始,按照頂點v1,v2,…,vn的次序,分別以每個頂點vk(1≤k≤n)作為新考慮的中間點,在第k-1次運算Ak-1 (A(0)為原圖的鄰接矩陣G) 的基礎上,求出每對頂點vi到vj的最短路徑長度計算公式為:
Floyd演算法:
proc shortpath_floyd;
begin
for i:=1 to n do for j:=1 to n do
begin
length[i,j]:=cost[i,j];
if length[i,j]<max then path[i,j]:=[i]+[j];
end;
for k:=1 to n do for i:=1 to n do for j:=1 to n do
if length[i,k]+length[k,j]<length[i,j] then
begin
length[i,j]:=length[i,k]+length[k,j];
path[i,j]:=path[i,k]+path[k,j];
end;
end;
其中:cost為鄰接矩陣;
path[i,j]:表示頂點i到j的最短路徑;
length[i,j]:
練習7:Floyd演算法練習
【問題描述】從文件中讀入帶權圖的信息,按Dijkstra演算法根據給定源點求出從源點到該圖中其餘頂點的最短路徑。
【輸入文件】第一行:一個整數L:L=0表示無向圖,L=1表示有向圖;第二行三個整數m、n,分別表示圖的結點數和圖中的邊數。以下n行表示n條邊:每一行三個數x、y和z,z表示x與y之間邊的權值。第n+2行:整數R,以下R行每行一個整數表示頂點標號作為源點。
【輸出文件】共R行,每一行的數據表示源點到其餘頂點的距離,按頂點編號由小大輸出,如果沒有路徑,輸出-1。
【示例】輸入:1 輸出:-1 10 50 30 60
6 8 -1 –1 –1 20 30
1 3 10
1 5 30
1 6 100
2 3 5
3 4 50
4 6 10
5 4 20
5 6 60
2
1
5
❸ 經典樹與圖論(最小生成樹、哈夫曼樹、最短路徑問題---Dijkstra演算法)
最小生成樹:在連通網的所有生成樹中,所有邊的代價和最小的生成樹,稱為最小生成樹。
1.Kruskal演算法
此演算法可以稱為「加邊法」,初始最小生成樹邊數為0,每迭代一次就選擇一條滿足條件的最小代價邊,加入到最小生成樹的邊集合里。
Prim演算法
此演算法可以稱為「加點法」,每次迭代選擇代價最小的邊對應的點,加入到最小生成樹中。演算法從某一個頂點s開始,逐漸長大覆蓋整個連通網的所有頂點。
1.圖的所有頂點集合為VV;初始令集合u={s},v=V−uu={s},v=V−u;
2.在兩個集合u,vu,v能夠組成的邊中,選擇一條代價最小的邊(u0,v0)(u0,v0),加入到最小生成樹中,並把v0v0並入到集合u中。
3.重復上述步驟,直到最小生成樹有n-1條邊或者n個頂點為止。
哈夫曼樹又稱最優二叉樹。它是 n 個帶權葉子結點構成的所有二叉樹中,帶權路徑長度 WPL 最小的二叉樹。
假設有n個權值,則構造出的哈夫曼樹有n個葉子結點。 n個權值分別設為 w1、w2、…、wn,則哈夫曼樹的構造規則為:
(1) 將w1、w2、…,wn看成是有n 棵樹的森林(每棵樹僅有一個結點);
(2) 在森林中選出兩個根結點的權值最小的樹合並,作為一棵新樹的左、右子樹,且新樹的根結點權值為其左、右子樹根結點權值之和;
(3)從森林中刪除選取的兩棵樹,並將新樹加入森林;
(4)重復(2)、(3)步,直到森林中只剩一棵樹為止,該樹即為所求得的哈夫曼樹。
注意:為了使得到的哈夫曼樹的結構盡量唯一,通常規定生成的哈夫曼樹中每個結點的左子樹根結點的權小於等於右子樹根結點的權。
在電報通信中,電文是以二進制的0、1序列傳送的,每個字元對應一個二進制編碼,為了縮短電文的總長度,採用不等長編碼方式,構造哈夫曼樹,
將每個字元的出現頻率作為字元結點的權值賦予葉子結點,每個分支結點的左右分支分別用0和1編碼,從樹根結點到每個葉子結點的路徑上
所經分支的0、1編碼序列等於該葉子結點的二進制編碼。如上文所示的哈夫曼編碼如下:
最短路徑問題介紹
問題解釋:
從圖中的某個頂點出發到達另外一個頂點的所經過的邊的權重和最小的一條路徑,稱為最短路徑。
初始狀態:S是已計算出最短路徑的頂點集合,U是未計算除最短路徑的頂點的集合!
第1步:將頂點D加入到S中。
此時,S={D(0)}, U={A(∞),B(∞),C(3),E(4),F(∞),G(∞)}。 注:C(3)表示C到起點D的距離是3。
第2步:將頂點C加入到S中。
上一步操作之後,U中頂點C到起點D的距離最短;因此,將C加入到S中,同時更新U中頂點的距離。以頂點F為例,之前F到D的距離為∞;但是將C加入到S之後,F到D的距離為9=(F,C)+(C,D)。
此時,S={D(0),C(3)}, U={A(∞),B(23),E(4),F(9),G(∞)}。
第3步:將頂點E加入到S中。
上一步操作之後,U中頂點E到起點D的距離最短;因此,將E加入到S中,同時更新U中頂點的距離。還是以頂點F為例,之前F到D的距離為9;但是將E加入到S之後,F到D的距離為6=(F,E)+(E,D)。
此時,S={D(0),C(3),E(4)}, U={A(∞),B(23),F(6),G(12)}。
第4步:將頂點F加入到S中。
此時,S={D(0),C(3),E(4),F(6)}, U={A(22),B(13),G(12)}。
第5步:將頂點G加入到S中。
此時,S={D(0),C(3),E(4),F(6),G(12)}, U={A(22),B(13)}。
第6步:將頂點B加入到S中。
此時,S={D(0),C(3),E(4),F(6),G(12),B(13)}, U={A(22)}。
第7步:將頂點A加入到S中。
此時,S={D(0),C(3),E(4),F(6),G(12),B(13),A(22)}。
此時,起點D到各個頂點的最短距離就計算出來了:A(22) B(13) C(3) D(0) E(4) F(6) G(12)。
❹ 用堆來實現計算單源最短路的迪傑斯特拉(Djisktra)演算法
//最近剛寫了這個程序,希望對你有幫助
#include<stdafx.h>
#include<stdio.h>
#include<stdlib.h>
#define MAXNODE 30 //定義最大節點數
#define MAXCOST 1000 //如果兩點間無路勁,則設MAXCOST
int dist[MAXNODE],cost[MAXNODE][MAXNODE],n=6; //為實際節點數
//dijkstra演算法求單源最短路徑,這個函數就沒加註釋了,需要自己理解。
void dijkstra(int v0) //v0為起始節點
{
int s[MAXNODE];
int mindis,dis,i,j,u;
for(i=1;i<=n;i++)
{
dist[i]=cost[v0][i];
s[i]=0;
}
s[v0]=1;
for(i=1;i<=n;i++)
{
mindis=MAXCOST;
for(j=1;j<=n;j++)
{
if(s[j]==0 && dist[j]<mindis)
{
u=j;
mindis=dist[j];
}
}
s[u]=1;
for(j=1;j<=n;j++)
if(s[j]==0)
{
dis=dist[u]+cost[u][j];
dist[j]=(dist[j]<dis)?dist[j]:dis;
}
}
}
//自定義display_path函數,輸出各頂點最短路徑
void display_path(int v0)
{
int i;
printf("\n頂點 %d 到各頂點的最短路徑長度如下:\n",v0);
for(i=1;i<=n;i++)
{
printf(" (v%d->v%d):",v0,i);
if(dist[i]==MAXCOST)
printf("無路徑");
else
printf("%d\n",dist[i]);
}
}
//主函數中各個定點的cost值可以根據實際路勁修改
void main()
{
int i,j,v0=1;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
cost[i][j]=((i==j)?0:MAXCOST);
cost[1][2]=10;
cost[1][4]=30;
cost[1][5]=100;
cost[1][6]=20;
cost[2][3]=50;
cost[3][5]=10;
cost[4][3]=20;
cost[4][5]=60;
cost[5][6]=30;
dijkstra(v0);
display_path(v0);
}