图的最短路径--单源、多源最短路径
最短路徑
–在網(wǎng)絡(luò)中,求兩個(gè)不同頂點(diǎn)之間的所有路徑中,邊的權(quán)值之和最小的路勁。
單源最短路徑
–從某固定源點(diǎn)出發(fā)的最短路徑
無(wú)權(quán)圖的最短路徑
- 按照路徑長(zhǎng)度遞增的順序找出源點(diǎn)到各個(gè)頂點(diǎn)的最短路徑
-
類(lèi)似于BFS-寬度優(yōu)先遍歷,可以通過(guò)隊(duì)列來(lái)實(shí)現(xiàn),
- 先讓頂點(diǎn)入隊(duì),循環(huán)執(zhí)行下列步驟
- 出隊(duì)首元素,訪問(wèn)其所有鄰接點(diǎn)
- 標(biāo)明源點(diǎn)到這些鄰接點(diǎn)的路徑長(zhǎng)度,并將其入隊(duì)
有權(quán)圖的最短路徑
Dijkstra算法
-
令第一組為集合S={源點(diǎn)+已經(jīng)確定最短路徑的頂點(diǎn)vi}
-
令第二組為未在集合S中的頂點(diǎn)v,定義length成員為源點(diǎn)s到v的最短路徑長(zhǎng)度,該路徑除了終點(diǎn)v以外,所有頂點(diǎn)都必須是集合S中的頂點(diǎn)。即{s—>(vi∈S)—>v}的最小長(zhǎng)度
-
每次對(duì)未在第一組集合S的頂點(diǎn),選一個(gè)length最小的頂點(diǎn)(記為v),將其增加至集合S中。
-
如何選這個(gè)頂點(diǎn)V–用一個(gè)最小堆H(剛開(kāi)始只有源點(diǎn)),每次找出并刪除一個(gè)length值最小的頂點(diǎn)V,這里這個(gè)找到并刪除的頂點(diǎn)V就是每次加入集合S的頂點(diǎn),然后找到V的所有鄰接點(diǎn),對(duì)其鄰接點(diǎn)的length值進(jìn)行賦值,然后加入最小堆H里,往復(fù)循環(huán),直至最小堆空了(最小堆空了,即所有頂點(diǎn)都加入了集合S)–看程序就明白了
-
選進(jìn)去的時(shí)候要注意什么–增加v進(jìn)集合S中會(huì)影響頂點(diǎn)w(w為v的鄰接點(diǎn))的length值,因?yàn)槎嗔藗€(gè)頂點(diǎn)v,可能會(huì)改變s—w的走法,原先不經(jīng)過(guò)v,現(xiàn)在最短路徑經(jīng)過(guò)v,即需要考慮
length[w],與length[v] + e.weigth的大小關(guān)系--------(1)
-
-
直到把所有頂點(diǎn)都送進(jìn)集合S中
設(shè)源點(diǎn)為V0,則按照Dijkstra算法的最短路徑求解過(guò)程如下
注:初始的時(shí)候只有集合S只有源點(diǎn)s,因此只有到點(diǎn)v1、v2有路徑,路徑長(zhǎng)度用length表示,,沒(méi)有路徑則length為∞,路徑的終點(diǎn)的前一個(gè)點(diǎn)用pre表示。
```C++ class Dist { // Dist類(lèi),用于保存最短路徑信息public:int index; // 結(jié)點(diǎn)的索引值int length; // 當(dāng)前最短路徑長(zhǎng)度int pre; // 路徑最后經(jīng)過(guò)的結(jié)點(diǎn) };void Dijkstra(Graph& G, int s, Dist* &D) { // s是源點(diǎn)D = new Dist[G. VerticesNum()]; // 開(kāi)辟空間給類(lèi)Dist,用來(lái)記錄當(dāng)前最短路徑長(zhǎng)度for (int i = 0; i < G.VerticesNum(); i++) { // 初始化,將圖中所有頂點(diǎn)的標(biāo)志位記為未訪問(wèn),將Dist類(lèi)的索引值記為頂點(diǎn)號(hào),將頂點(diǎn)到源點(diǎn)的length置為∞。G.Mark[i] = UNVISITED; D[i].index = i; D[i].length = INFINITE;D[i].pre = s;}D[s].length = 0; // 初始化,源點(diǎn)自身的路徑長(zhǎng)度置為0MinHeap<Dist> H(G. EdgesNum()); // 最小堆用于找出集合S中到源點(diǎn)的路徑最短的點(diǎn),最小堆存放Dist類(lèi)元素,共有G.EdgesNum個(gè)長(zhǎng)度H.Insert(D[s]); //將源點(diǎn)放入最小堆for (i = 0; i < G.VerticesNum(); i++) {bool FOUND = false;Dist d;while (!H.isEmpty()) {d = H.RemoveMin(); //獲得集合S中到源點(diǎn)s路徑長(zhǎng)度最小的結(jié)點(diǎn),并刪除if (G.Mark[d.index] == UNVISITED) { //如果未訪問(wèn)過(guò)則跳出循環(huán)FOUND = true; break;} }if (!FOUND) break; // 若沒(méi)有符合條件的最短路徑則跳出本次循環(huán)int v = d.index; G.Mark[v] = VISITED; // 將標(biāo)記位設(shè)置為 VISITEDfor (Edge e = G.FirstEdge(v); G.IsEdge(e); e = G.NextEdge(e)) // 對(duì)最小堆中到源點(diǎn)路徑最短的頂點(diǎn)v,求他的每個(gè)鄰接點(diǎn),并考慮是否需要改變其最小距離--刷新最短路,然后將其加入最小堆if (D[G.ToVertex(e)].length > (D[v].length+G.Weight(e))) {//這里第一次執(zhí)行時(shí),因?yàn)閯傞_(kāi)始最小堆里只有源點(diǎn),其他頂點(diǎn)到源點(diǎn)的length都為∞,執(zhí)行完后V1、V2的length值才改變D[G.ToVertex(e)].length = D[v].length + G.Weight(e);D[G.ToVertex(e)].pre = v;H.Insert(D[G.ToVertex(e)]);} } }Dijkstra算法時(shí)間復(fù)雜度
T = O ( ∣ V ∣ l o g ∣ V ∣ + ∣ E ∣ l o g ∣ V ∣ ) = O ( ∣ E ∣ l o g ∣ V ∣ ) T = O( |V| log|V| + |E| log|V| ) = O( |E| log|V| ) T=O(∣V∣log∣V∣+∣E∣log∣V∣)=O(∣E∣log∣V∣)
前半部分為在最小堆里找V次,依次復(fù)雜度為logV,后半部分是往最小堆里插入Dist,一次最多有可能插E次(極限情況,一個(gè)頂點(diǎn)有E條邊)
Dijkstra使用條件
- 圖中不能出現(xiàn)有總權(quán)值為負(fù)值的回路
- 如果存在負(fù)值邊也有可能發(fā)生計(jì)算錯(cuò)誤
- 持負(fù)權(quán)值的最短路徑算法有Ford算法、SPFA算法
多源最短路徑
–求每對(duì)頂點(diǎn)間的最短路徑
Floyd算法
-
Dk[i] [j]表示路徑{ i —> { l<= k } —> j }的最小長(zhǎng)度(i、j、l、k表示頂點(diǎn)編號(hào))
-
首先用矩陣D0表示該圖的鄰接矩陣
-
在矩陣D0上做n次迭代
-
如何迭代呢–當(dāng)Dk-1已經(jīng)完成,遞推到Dk時(shí),
-
若k?最短路徑{ i —> { l<= k } —> j },(即i到j(luò)的最短路徑不經(jīng)過(guò)k)則Dk=Dk-1
-
若k∈最短路徑{ i —> { l<= k } —> j }(即i到j(luò)的最短路徑經(jīng)過(guò)k),則該最短路徑由兩條路徑組成:
D ( k ) [ i ] [ j ] = D ( k ? 1 ) [ i ] [ k ] + D ( k ? 1 ) [ k ] [ j ] D(k)[i][j]=D(k-1)[i][k]+D(k-1)[k][j] D(k)[i][j]=D(k?1)[i][k]+D(k?1)[k][j]
-
Floyd算法時(shí)間復(fù)雜度
T = O ( ∣ V ∣ 3 ) T = O( |V|3 ) T=O(∣V∣3)
總結(jié)
以上是生活随笔為你收集整理的图的最短路径--单源、多源最短路径的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 酒店客房管理系统之系统实施--数据库
- 下一篇: 【计算机组成原理】一、基本运算器实验