天天看點

算法基礎第三章搜尋與圖論

假設頂點是\(V_0到V_5\) 六個點,開始時候是沒有連線的,但是已知能互相到達的頂點之間的邊權。

步驟是每次從頂點0開始查找,找出距離頂點最短的點,然後标記該點為true,再查詢該點能直達的其他點加上邊權會不會比原先記錄的距離值小--->即更新最短距離;周遊完了所有從該點能到的點後再次回到起點。

樹與圖的存儲

樹是一種特殊的圖,與圖的存儲方式相同。

對于無向圖中的邊ab,存儲兩條有向邊a->b, b->a。

是以我們可以隻考慮有向圖的存儲。

(1) 鄰接矩陣:<code>g[a][b]</code> 存儲邊<code>a-&gt;b</code>

(2) 鄰接表:

// 對于每個點k,開一個單連結清單,存儲k所有可以走到的點。h[k]存儲這個單連結清單的頭結點

當這個圖是n個點 m條邊的無向圖時,那麼<code>m可能和 n的2倍差不多</code>:

這時, 不妨設<code>M = 2 * N</code>

<code>h[N]</code> <code>e[M]、ne[M]</code>

時間複雜度<code>O(n+m)</code>,n表示點數,m表示邊數

(1)深度優先周遊

(2)橫向優先搜尋

樸素版dijkstra算法适合稠密圖,用鄰接矩陣存儲

堆優化版适合稀疏圖,用鄰接表存儲 點的範圍較大--稀疏圖

時間複雜是 \(O(n^2+m)\), n 表示點數,m 表示邊數

1、當到一個時間點時,圖上部分的點的最短距離已确定,部分點的最短距離未确定。

2、選一個所有未确定點中離源點最近的點,把他認為成最短距離。

3、再把這個點所有出邊周遊一邊,更新所有的點。

算法設計:貪心

st[]更确切的含義是某個點是否已經更新過其他點,st最短路确定而不是它的最短距離是否已經确定。是以更新其他點的距離時,前面更新過的點也要更新 其實也可以加上<code>if(!st[j])</code>

思路:

集合S中的點表示已經找到最短路徑

一些問題:

在更新dist時,有可能兩個點重複進堆,有的點它既是a的鄰接點又是b的鄰接點,這樣的點可能會在更新a的鄰接點時加進一次,右在更新b的鄰接點的時再進入一次,這樣隊列中就有兩個一樣的點,雖然距離不同,是以用<code>st</code> 數組對已經找出最短路徑的點進行标記,避免重複計算

題目:有邊數限制的最短路a

單源最短路徑算法

對于帶權有向圖 G = (V, E),Dijkstra 算法要求圖 G 中邊的權值均為非負,而 Bellman-Ford 算法能适應一般的情況(即存在負權邊的情況)。一個實作的很好的 Dijkstra 算法比 Bellman-Ford 算法的運作時間要低。

設計:動态規劃

時間複雜度:\(O(V*E)\) 頂點數 邊數, \(n頂點數,m邊數\)

了解:對所有邊進行\(n-1\)次松弛操作,因為在一個含有n個頂點的圖中,任意兩點之間的最短路徑最多包含n-1條邊,

換句話說,第1輪在所有邊進行松弛後,得到的是源點最多經過1條邊到達其他頂點的最短距離;

第2輪在對所有的邊進行松弛後,得到的是源點最多經過2條邊到達其他頂點的最短距離;

算法描述:

1、\(dist[N]\)數組表示源頂點到所有頂點的距離,初始化為\(infinte\),\(dist[1][1]=0\),

2、計算最短路徑,執行\(V-1\)次周遊

對于圖中的每條邊:如果起點到u的距離d加上權值w小于到終點v的距離,更新終點v的距離值d

\(if(dist[b]&gt;dist[a]+w) dist[b]=dist[a]+w\)

例如以下加上一個拷貝數組就可以求最多經過k條邊的最短距離

判斷是否有負權環,再對邊進行一次額外的周遊,如果還能更新說明仍然存在一條邊使得兩點距離更短,事實上再更新多次還是有更新的情況。

注意:

如果不限制邊數,直接求最短路,不需要拷貝數組

如果限制邊數,則需要拷貝數組

為什麼是<code>&gt; 0x3f3f3f3f / 2</code> (主要還是因為每條邊都周遊了,周遊了很多無用的邊)

題目: spafa判斷負環

spfa求最短路

https://blog.csdn.net/qq_35644234/article/details/61614581 這篇部落格給出了過程

https://www.cnblogs.com/acioi/p/11694294.html spfa求負環的解釋

時間複雜度 平均情況下 \(O(m)\),最壞情況下 \(O(nm)\), n 表示點數,m 表示邊數

\(SPFA算法\)是對上面的\(bellmanford\)算法的隊列優化

算法描述:首先建立一個隊列,初始隊列裡隻有起始點,建立一個表格記錄起始點到所有點的最短路徑(初始值賦為極大值),然後進行松弛操作,依次用隊列中的點去重新整理起始點到所有點的最短路,如果重新整理成功且被重新整理點不在隊列中則把其加入到隊列中。

求負環:如果某個點進入隊列的次數超過N次則存在負環(N為圖的頂點數)

最優解法:用一個cnt[i] 數組記錄目前到 到 i 點的最短路徑上經過的點的數量,如果 出現<code>cnt[i] &gt; n</code>說明出現了負環。也可統計邊數,當<code>邊數 &gt;= n</code>時也是出現了負環。

<code>st</code>數組的作用隻是記錄目前有哪些點在隊列中

\(Floyd\)算法屬于暴力求解,時間複雜度\(O(n^3)\),\(n\)表示點數

判斷無最短路徑的方法是<code>&gt; inf / 2</code>, 原因:加入求1~6個點的距離,6是終點,

<code>d[1][5] = 0x3f</code>,1到5不可達,此時 <code>d[5][6] = -4</code>, <code>d[1][6] = d[1][5] + d[5][6] !=0x3f</code>但是大于0x3f/2。此時1到6是不可達的。

題目:有向無環圖的拓撲序列

在圖論中,拓撲排序是一個有向無環圖的所有頂點的線性序列:

1、每個頂點出現一次

2、若存在一條從A到B的路徑,那麼在序列中頂點A在B的前面。

一個有向無環圖一定至少存在一個入度為0的點

如何求拓撲序列?

拓撲序列中,所有的邊都是從前往後的,是以入度為0的點都可以作為起點,将所有入度為0的點入隊,因為前面沒有點指向它,它隻能指向後面的點

入隊之後,将它指向的終點的入度減去1

入度為0的點入隊

周遊t的所有出邊

完整模闆

繼續閱讀