【数据结构】图的应用(普利姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法、拓扑排序)
最小生成樹
什么是最小生成樹
- 是一棵樹
- 無回路
- |V|個(gè)頂點(diǎn)一定有|V|-1條邊 - 是生成樹
- 包含全部頂點(diǎn)
- |V|-1條邊全在圖里
貪心算法
什么是“貪”:每一步都要最好的
什么是“好”:權(quán)重最小的邊
需要約束:
普利姆(Prim)算法——讓一棵小樹長大
需要維護(hù)兩個(gè)數(shù)組:lowcost[n] 、adjvex[n](n是圖中的頂點(diǎn)數(shù))
①從圖中找第一個(gè)起始頂點(diǎn)v0,作為生成樹的第一個(gè)頂點(diǎn),然后從這個(gè)頂點(diǎn)到其他頂點(diǎn)的所有邊中選一條權(quán)值最小的邊。然后把這條邊的另一個(gè)頂點(diǎn)v和這條邊加入到生成樹中。
②對剩下的其他所有頂點(diǎn),分別檢查這些頂點(diǎn)與頂點(diǎn)v的權(quán)值是否比這些頂點(diǎn)在lowcost數(shù)組中對應(yīng)的權(quán)值小,如果更小,則用較小的權(quán)值更新lowcost數(shù)組。
③從更新后的lowcost數(shù)組中繼續(xù)挑選權(quán)值最小而且不在生成樹中的邊,然后加入到生成樹。
④反復(fù)執(zhí)行②③直到所有所有頂點(diǎn)都加入到生成樹中。
視頻講解
算法分析
普利姆算法是雙重循環(huán),外層循環(huán)次數(shù)為n-1,內(nèi)層并列的兩個(gè)循環(huán)次數(shù)都是n。故普利姆算法時(shí)間復(fù)雜度為O(n2)
而且時(shí)間復(fù)雜度只和n有關(guān),所以適合稠密圖
克魯斯卡爾(Kruskal)算法——把森林合并成樹
我們知道生成樹是包含n個(gè)頂點(diǎn),n-1條邊的
換一種思路,我們可以從網(wǎng)中的邊這個(gè)角度,找最小權(quán)值的邊,直到找到n-1條邊。
思路
將圖中邊按照權(quán)值從小到大排列,然后從最小的邊開始掃描,設(shè)置一個(gè)邊的集合來記錄,如果該邊并入不構(gòu)成回路的話,則將該邊并入當(dāng)前生成樹。直到所有的邊都檢測完為止。
排列: 請參考→ 堆
不構(gòu)成回路:請參考→ 并查集
看一個(gè)例子,視頻講解
克魯斯卡爾算法操作分為對邊的權(quán)值排序部分和一個(gè)單重for循環(huán),它們是并列關(guān)系,由于排序耗費(fèi)時(shí)間大于單重循環(huán),所以克魯斯卡爾算法的主要時(shí)間耗費(fèi)在排序上。排序和圖中邊的數(shù)量有關(guān)系,所以適合稀疏圖。
最短路徑
問題分類
單源最短路徑問題:從某固定源點(diǎn)出發(fā),求其到所有其他頂點(diǎn)的最短路徑
多源最短路徑問題:求任意兩頂點(diǎn)間的最短路徑
迪杰斯特拉算法
該算法設(shè)置一個(gè)集合S記錄已求得的最短路徑的頂點(diǎn),可用一個(gè)數(shù)組s[]來實(shí)現(xiàn),初始化為0,當(dāng)s[vi]=1時(shí)表示將頂點(diǎn)vi放入S中,初始時(shí)把源點(diǎn)v0放入S中。此外,在構(gòu)造過程中還設(shè)置了兩個(gè)輔助數(shù)組:
dist[]:記錄了從源點(diǎn)v0到其他各頂點(diǎn)當(dāng)前的最短路徑長度,dist[i]初值為arcs[v0][i]。
path[]:path[i]表示從源點(diǎn)到頂點(diǎn)i之間的最短路徑的前驅(qū)結(jié)點(diǎn),在算法結(jié)束時(shí),可根據(jù)其值追溯得到源點(diǎn)v0到頂點(diǎn)vi的最短路徑。
假設(shè)從頂點(diǎn)0出發(fā),也就是頂點(diǎn)0為源點(diǎn),集合S最初只包含頂點(diǎn)0,鄰接矩陣arcs表示帶權(quán)有向圖,arcs[i][j]表示有向邊<i,j>的權(quán)值,若不存在有向邊<i,j>,則arcs[i][j]為∞。Dijkstra算法的步驟如下:
1)初始化:集合S初始為{0},dist[]的初始值dist[i]=arcs[0][i],i=1,2,…,n-1。
2)找出dist[]中的最小值dist[j],將頂點(diǎn)j加入集合S,即修改s[vj]=1。
3)修改從v0出發(fā)到集合V-S上任一頂點(diǎn)vk可達(dá)的最短路徑長度:如果dist[j] + arcs[j][k]< dist[k],則令dist[k]=dist[j] + arcs[j][k]。另外更新path[k]=j(也就是頂點(diǎn)j加入集合之后如果有新的路徑使得到頂點(diǎn)k路徑變短的話就將到頂點(diǎn)k的路徑長度修改成較短的)
4)重復(fù)2)~3)操作共n-1次,直到所有的頂點(diǎn)都包含在S中。
具體過程可參考 視頻講解
時(shí)間復(fù)雜度
迪杰斯特拉算法的核心部分在于一個(gè)雙重循環(huán),這個(gè)雙重循環(huán)的內(nèi)循環(huán)又是兩個(gè)并列的單重for循環(huán)組成(找距離最小頂點(diǎn)和更新距離),任意取其中一個(gè)循環(huán)中的操作為基本操作,都可以得出迪杰斯特拉算法的時(shí)間復(fù)雜度為O(n2) 其中n為圖中的頂點(diǎn)數(shù)。
迪杰斯特拉算法不能用于權(quán)值有負(fù)數(shù)的圖,不然結(jié)果會(huì)出錯(cuò)!
弗洛伊德算法
算法思想
遞推產(chǎn)生一個(gè)n階方陣序列A(?1),A(0),…,A(k),…,A(n?1)
其中A(k)[i][j]表示從頂點(diǎn)vi到頂點(diǎn)vj的路徑長度,k表示繞行第k個(gè)頂點(diǎn)的運(yùn)算步驟。初始時(shí),對于任意兩個(gè)頂點(diǎn)vi和vj,若它們之間存在邊,則以此邊上的權(quán)值作為它們之間的最短路徑長度;若它們之間不存在有向邊,則以∞作為它們之間的最短路徑長度。以后逐步嘗試在原路徑中加入頂點(diǎn)k(k=0,1,…,n-1)作為中間頂點(diǎn)。如果增加中間頂點(diǎn)后,得到的路徑比原來的路徑長度減少了,則以此新路徑代替原路徑。
弗洛伊德算法的核心為一個(gè)三重循環(huán),所以時(shí)間復(fù)雜度為O(n3) 其中n是圖中的頂點(diǎn)數(shù)。
拓?fù)渑判?/h3>
AOV
如果我們把每個(gè)環(huán)節(jié)看成圖中一個(gè)頂點(diǎn),在這樣一個(gè)有向圖中,用頂點(diǎn)表示活動(dòng),用弧表示活動(dòng)之間的優(yōu)先關(guān)系,那么這樣的有向圖稱為AOV網(wǎng)(Activity On Vertex)
由于弧是用來表示活動(dòng)之間的優(yōu)先關(guān)系,或者說AOV網(wǎng)具有實(shí)際的意義,那么AOV網(wǎng)顯然是不能有回路的
有向無環(huán)圖也叫做DAG圖
拓?fù)湫蛄惺菍D中所有的頂點(diǎn),如果存在一條從頂點(diǎn)A到頂點(diǎn)B的路徑,那么在排序中頂點(diǎn)A出現(xiàn)在頂點(diǎn)B的前面。
拓?fù)渑判蚓褪菍σ粋€(gè)有向圖構(gòu)造拓?fù)湫蛄械倪^程,構(gòu)造會(huì)有兩種結(jié)果:
拓?fù)渑判蛩惴?/h4>
從AOV網(wǎng)中選擇一個(gè)入度為0的頂點(diǎn)輸出,然后刪去此頂點(diǎn),并刪除以此頂點(diǎn)為弧尾的弧。重復(fù)這個(gè)步驟直到輸出圖中全部頂點(diǎn),或者找不到入度為0的頂點(diǎn)為止。
一個(gè)DAG的拓?fù)渑判虿晃ㄒ?br /> 由于拓?fù)渑判蛐枰獎(jiǎng)h除邊和頂點(diǎn),所以使用鄰接表存儲圖比較方便。
拓?fù)渑判驅(qū)OV圖需要打印圖中所有頂點(diǎn),而且由于要?jiǎng)h除邊(實(shí)際沒有刪除,只是尋找入度為0的頂點(diǎn))所以對所有邊也要進(jìn)行掃描,所以這個(gè)算法的時(shí)間復(fù)雜度為O(∣V∣+∣E∣)O (|V|+|E|)O(∣V∣+∣E∣)
只要按照序號從小到大或者從大到小的順序就能得到這個(gè)鄰接矩陣為三角矩陣的圖的拓?fù)湫蛄小?/li>
上三角:0 1 2 3 下三角:3 2 1 0
關(guān)鍵路徑
AOE(Activity On Edge):在一個(gè)表示工程的帶權(quán)有向圖中,用頂點(diǎn)表示事件,用有向邊表示活動(dòng),用邊上的權(quán)值表示活動(dòng)的持續(xù)時(shí)間,這種有向圖的邊表示活動(dòng)的網(wǎng)稱為AOE網(wǎng)。
活動(dòng)是在邊上,邊上的權(quán)值表示的是這個(gè)活動(dòng)所需要耗費(fèi)的時(shí)間。AOE網(wǎng)是在AOE的基礎(chǔ)上來分析工程的最少需要時(shí)間。或者是為了縮短工期,需要找出哪些活動(dòng)是要加快的。
開始時(shí)間為0 設(shè)定造好各個(gè)模塊的時(shí)間為5 因?yàn)樽铋L路徑為5
造輪子最早發(fā)生的時(shí)間:0 最晚發(fā)生的時(shí)間:3
造零件最早發(fā)生的時(shí)間:0 最晚發(fā)生的時(shí)間:4
造發(fā)動(dòng)機(jī)最早發(fā)生的時(shí)間:0 最晚發(fā)生的時(shí)間:0
關(guān)鍵活動(dòng)的最早發(fā)生時(shí)間和最晚發(fā)生時(shí)間是一樣的!
例子:
參考資料
王道數(shù)據(jù)結(jié)構(gòu)
超強(qiáng)干貨來襲 云風(fēng)專訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生總結(jié)
以上是生活随笔為你收集整理的【数据结构】图的应用(普利姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法、拓扑排序)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【数据结构】图的遍历(BFS和DFS)
- 下一篇: 【数据结构】KMP算法(c语言)