圖的定義:
很簡單,g(v,e), v、e分別表示點和邊的集合。
圖的表示:
主要有兩種,鄰接矩陣和鄰接表,前者空間複雜度,o(v2),後者為o(v+e)。因此,除非非常稠密的圖(邊非常多),一般後者優越於前者。
圖的遍歷:
寬度遍歷bfs(start): (1)佇列q=empty,陣列bool visited[v]=. q.push(start);
(2)while (!q.empty())
深度遍歷dfs(start): (1)棧s=empty, 陣列bool visited[v]=. s.push(start);
(2)while (!s.empty())
初看之下兩個演算法很 相似,主要區別在於乙個使用佇列,乙個使用棧,最終導致了遍歷的順序截然不同。佇列是先入先出,所以訪問u以後接下來就訪問u中未訪問過的鄰接結點;而棧的後進先出,當訪問u後,壓入了u的鄰接結點,在後面的迴圈中,首先訪問u的第乙個臨接點v,接下來又將v的鄰接點w壓入s,這樣接下來要訪問的自然是w 了。
最小生成樹:
一.prime演算法:
(1) 集合mst=t=empty,選取g中一結點u,t.add(u)
(2) 迴圈|v|-1次:選取一條這樣的邊e=min t.add(y);mst.add(e);
(3)mst即為所求
二. kruskal演算法
(1)將g中所有的邊排序並放入集合h中,初始化集合mst=empty,初始化不相交集合t=, ...}},也即t中每個點為乙個集合。
(2) 依次取h中的最短邊e(u,v),如果find-set(u)!=find-set(v)(也即u、v是否已經在一棵樹中),那麼union(u,v)(即u,v合併為乙個集合),mst.add(e);
(3)mst即為所求
這兩個演算法都是貪心演算法,區別在於每次選取邊的策略。證明該演算法的關鍵在於一點:如果mst是圖g的最小生成樹,那麼在子圖g'中包含的子生成樹mst' 也必然是g'的最小生成樹。這個很容易反正,假設不成立,那麼g'有一棵權重和更小的生成樹,用它替換掉mst',那麼對於g我們就找到了比mst更小的生成樹,顯然這與我們的假設(mst是最小生成樹)矛盾了。
理解了這個關鍵點,演算法的正確性就好理解多了。對於prime,t於v/t兩個點集都會各自有一棵生成樹,最後要連起來構成一棵大的生成樹,那麼顯然要選兩者之間的最短的那條邊了。對於kruskal演算法,如果當前選取的邊沒有引起環路,那麼正確性是顯然的(對給定點集依次選最小的邊構成一棵樹當然是最小生成樹了),如果導致了環路,那麼說明兩個點都在該點集裡,由於已經構成了樹(否則也不可能導致環路)並且一直都是挑盡可能小的,所以肯定是最小生成樹。
最短路徑:
這裡的演算法基本是基於動態規劃和貪心演算法的,經典演算法有很多個,主要區別在於:有的是通用的,有的是針對某一類圖的,例如,無負環的圖,或者無負權邊的圖等。
單源最短路徑
(1) 通用(bellman-ford演算法):
(2) 無負權邊的圖(dijkstra演算法):
(3) 無環有向圖(dag) 所有結點間最短路徑:
(1)floyd-warshall演算法:
(2) johnson演算法:
專題四總結
專題四總結 專題四圖演算法,到目前為止學習了並查集,最小生成樹,最短路。並查集找父節點 int find int x 練習四1003 另一種 int find int a 練習四1022 最小生成樹只會用kruscul 演算法,用這乙個能做不少題了也就懶得學 primer.double kru re...
專題四 總結
在這一專題裡,首先了解了圖資料結構的一些基礎知識以及圖的儲存方式。圖的一些基礎知識包括 圖的概念,圖的一些部件的命名,以及一些基本的數量關係 圖的表示形式一般包括 矩陣,鄰接表 我感到最有趣的就是鄰接表的陣列表示形式了,開銷低且高效,感覺甚是神奇,下面貼出鄰接表的陣列表示形式 struct edge...
4專題四總結
專題四主要就是最短路徑問題。一般最短路徑有兩種演算法prim與kruscal演算法,然而這兩種演算法核心技術就是並查集。並查集。即 不相交集合 將編號分別為1 n的n個物件劃分 為不相交集合,在每個集合中,選擇其中某個元素代表所在集合。常見兩種操作 合併兩個集合查詢某元素屬於哪個集合 模板演算法 v...