最段路(基礎未優化)

最短路(基礎未優化)

寫在前面

寫最短路我猶豫了好久,由於最短路它涵蓋的內容不少(四個基礎算法),並且在基礎算法上還有許多不一樣的優化,甚至存邊都有幾種方式,就顯得特別複雜html

基本概念

最短路就是求在一個圖中一個點到指定點的最短路程(其實光看名字就猜的出來)。算法

幾個概念:數組

1.出發的點叫源點
2.一個點到另外一個點的路程(也能夠理解爲鏈接這兩個點的線段的長度)叫權
3.圖分兩種:有向圖和無向圖。有向圖就像單行道,只能去不能回。而無向圖就像雙行道,能夠去也能夠回。

還有一些例如鬆弛操做這些的概念會在下面進行解釋優化

基礎算法

存邊

對於一個圖,咱們須要把邊存儲下來。而存儲有兩種方式。spa

鄰接表

用一個二維數組來存儲。假如用數組k來存儲邊,則.net

k[i][j]

表示有一條從i到j的邊,權值爲code

k[i][j]

前向星

前向星比鄰接表省空間。
前向星分兩種,正常前向星(這名字是我取的,反正就是那個意思)和鏈式前向星。htm

正常前向星

思路是用3個數組,(這裏假設爲a,b,c)(可能有點難理解)
blog

a[m]=i
b[m]=j
c[m]=n(其中m爲常數)

就是表示從i到j有一條邊,權值爲n。
可是假如要排序的話,開三個數組很麻煩,因此我建議用結構體。而結構體的排序方法我在講最小生成樹之kruskarl的時候講過,這裏就再也不贅述。排序

鏈式前向星

這個是一種鏈式的存邊方法。假如要細講的話,可能又要講一篇文章了,下文也不會使用這個(主要是沒有正常前向星好理解,並且也比較複雜)。
感興趣的話能夠去看這篇博客

https://blog.csdn.net/acdreamers/article/details/16902023/

無向邊

以上講的都是存有向邊的方式。存無向邊其實區別不是很大。舉個例子相信一下就懂了。假如你要存一條從i到j的無向邊,你能夠這麼寫

k[i][j]=n;
k[j][i]=n;

就是把終點和起點換一下位置,把終點當起點,把起點當終點。

Floyd

這個算法有一大特色,那就是寫起來極其簡單(核心代碼只有5行),可是運行效率極低(o(n^3)),就讓它顯得很毒瘤,並且數據基本都會卡這種算法。
假如用二維數組

k[i][j]

來存儲從i到j的最段路。
初始化:先把k數組所有賦一個很大很大的值(方便後面比較)
floyd算法以下

for(int k=1;k<=n;k++)//k枚舉途經的節點
        for(int i=1;i<=n;i++)//i枚舉起點
            for(int j=1;j<=n;j++)//j枚舉終點
                if(d[i][j]!=inf&&d[i][k]!=inf)//假如i到k沒有邊,那麼假設不成立,同理,假如i到j沒有邊,這個假設也不成立
                    d[i][j]=min(d[i][k]+d[k][j],d[i][j]);//拿現有的從i到j的最段路和i途經k再到j的總路程作比較。(這就是爲何要把d[i][j]的初始值賦爲一個很大的數)

能夠看一下上面的註釋。。
floyd就是靠三重循環來枚舉起點、途經的點和終點。而後作鬆弛操做。(鬆弛操做就是比大小那一步,有點像三角形的三邊關係)拿現有的從i到j的最段路和i途經k再到j的總路程作比較。也算是一個dp。
這個算法沒啥優化(也沒啥可優化的)。能夠求單源最段路(有向圖中的最段路),也能夠求無向圖的最段路。不能對付負權

這裏順便引入兩個新概念:負權&環
負權:一條權值爲負的路(於現實生活中不存在)。
環:有些時候在有向圖中會同時有從i到j和從j到i的兩條路。他們就組成了一個環。
負權環就是一個很恐怖的東西了,你能夠靠負權環在環裏面不停的刷,這樣你的最短路。。。就成了無限的更短路。。。

dijkstra狄傑斯特拉算法

這是一個很經典的單源最短路的算法。
這個算法的描述我以爲在一本叫算法圖解的書中描述的比較詳細,在這裏就附上書的照片,以爲講得很通俗易懂,應該理解沒有什麼問題。(多圖預警)

clipboard.png

clipboard.png

clipboard.png

clipboard.png

clipboard.png

總之就是重複那四步,就能夠找出最優解。
算法效率比floyd不知道高到哪裏去了。。
不能對付負權

bellman-ford貝爾曼福德算法

這是一個能夠對付負權的算法
算法:

給你一個由v個點,e條邊的圖,和原點s

數組Distant[i]記錄從源點s到頂點i的路徑長度,初始化

數組Distant[n]爲, Distant[s]爲0;

如下操做循環執行至多n-1次,n爲頂點數:

對於每一條邊e(u, v),若是Distant[u] + w(u, v) < Distant[v],則另Distant[v] = Distant[u]+w(u, v)。w(u, v)爲邊e(u,v)的權值;

若上述操做沒有對Distant進行更新,說明最短路徑已經查找完畢,或者部分點不可達,跳出循環。不然執行下次循環;

爲了檢測圖中是否存在負環路,即權值之和小於0的環路。對於每一條邊e(u, v),若是存在Distant[u] + w(u, v) < Distant[v]的邊,則圖中存在負環路,便是說改圖沒法求出單源最短路徑。不然數組Distant[n]中記錄的就是源點s到各頂點的最短路徑長度。

可知,Bellman-Ford算法尋找單源最短路徑的時間複雜度爲O(V*E).

Bellman-Ford算法能夠大體分爲三個部分

第一,初始化全部點。每個點保存一個值,表示從原點到達這個點的距離,將原點的值設爲0,其它的點的值設爲無窮大(表示不可達)。

第二,進行循環,循環下標爲從1到n-1(n等於圖中點的個數)。在循環內部,遍歷全部的邊,進行鬆弛計算。

第三,遍歷途中全部的邊(edge(u,v)),判斷是否存在這樣狀況:
d(v) > d (u) + w(u,v)

則返回false,表示途中存在從源點可達的權爲負的迴路。

鬆弛操做以前講過,再看一遍複習一下

clipboard.png

鬆弛計算以前,點B的值是8,可是點A的值加上邊上的權重2,獲得5,比點B的值(8)小,因此,點B的值減少爲5。這個過程的意義是,找到了一條通向B點更短的路線,且該路線是先通過點A,而後經過權重爲2的邊,到達點B。
固然,若是出現如下狀況

clipboard.png

則不會修改點B的值,由於3+4>6。

spfa留着下次再講(和優化一塊兒)。
會了Bellman-Ford算法和狄傑斯特拉算法就已經能夠作一些最段路的題了,能夠練習一下。

重點理解:鬆弛操做(這玩意真的很重要,基本上最段路的精髓就是它了)

tks!
(參考文獻:https://www.cnblogs.com/tanky... , 算法圖解)

相關文章
相關標籤/搜索