無向圖:一種邊爲無序結點對的圖html
若是無向圖擁有最大數目的連通頂點的邊,則認爲這個無向圖是徹底的。java
- 對有n個頂點的無向圖,要使圖徹底就要求有n(n-1)/2條邊。
若是無向圖中的任意兩個頂點之間都存在一條路徑,則認爲這個無向圖是連通的。git
無向樹是一種連通的無環無向圖,其中一個元素被指定爲樹根。算法
有向圖(雙向圖):邊爲有序頂點對的圖,eg:邊(A,B)容許從A向B遊歷,但不容許反方向的遊歷。數組
若是有向圖的每兩個頂點之間都有兩條方向相反的邊鏈接,則認爲這個有向圖是徹底的。網絡
若是有向圖的任意兩個頂點之間都存在一條路徑,且鏈接兩個頂點的路徑中全部的邊都必須同向,則認爲這個有向圖是連通的。數據結構
有向樹是一種指定了一個元素做爲樹根的有向圖。學習
- 不存在其餘頂點到樹根的鏈接
- 每一個非樹根元素剛好有一個鏈接
- 樹根到每一個其餘頂點都有一條路徑
三元組來表示每條邊:起始頂點、終止頂點、權重測試
- 無向網絡的起始頂點與終止頂點能夠互換
- 有向圖必須包含每一個有向鏈接的三元組
廣度優先遍歷(相似樹的層序遍歷)優化
- 首先從一個未走到過的頂點做爲起始頂點,好比元素0頂點做爲起點。
- 沿0頂點的邊去嘗試訪問其它未走到過的頂點,首先發現2頂點尚未走到過,因而來到了2頂點。
- 返回到0頂點,再以0頂點做爲出發點繼續嘗試訪問其它未走到過的頂點,這樣來到了1頂點。
- 返回到0頂點,再以0頂點做爲出發點繼續嘗試訪問其它未走到過的頂點,這樣來到了5頂點。
- 可是,此時沿0頂點的邊,已經不能訪問到其它未走到過的頂點了,因此須要返回到2頂點。
- 沿2頂點的邊去嘗試訪問其它未走到過的頂點,(由於1頂點已經走過了)首先發現3頂點尚未走到過,因而來到了3頂點。
- 返回到2頂點,再以2頂點做爲出發點繼續嘗試訪問其它未走到過的頂點,這樣來到了4頂點。
- 可是,此時沿4頂點的邊,已經不能訪問到其它未走到過的頂點了,至此,全部頂點咱們都走到過了,遍歷結束。
深度優先遍歷(相似樹的前序遍歷)
- 首先從一個未走到過的頂點做爲起始頂點,好比元素0頂點做爲起點。
- 沿0頂點的邊去嘗試訪問其它未走到過的頂點,首先發現2頂點尚未走到過,因而來到了2頂點。
- 再以2頂點做爲出發點繼續嘗試訪問其它未走到過的頂點,這樣又來到了1頂點。
- 再以1號頂點做爲出發點繼續嘗試訪問其它未走到過的頂點。
- 可是,此時沿1頂點的邊,已經不能訪問到其它未走到過的頂點了,因此須要返回到2頂點。
- 返回到2號頂點後,以2頂點做爲出發點繼續嘗試訪問其它未走到過的頂點,此時又會來到3頂點,再以3號頂點做爲出發點繼續訪問其它未走到過的頂點,因而又來到了5號頂點。
- 可是,此時沿5頂點的邊,已經不能訪問到其它未走到過的頂點了,因此須要返回到3頂點。
- 返回到3頂點後,以3頂點做爲出發點繼續嘗試訪問其它未走到過的頂點,此時會來到4頂點,再以4頂點做爲出發點繼續訪問其它未走到過的頂點。
- 可是,此時沿4頂點的邊,已經不能訪問到其它未走到過的頂點了,至此,全部頂點咱們都走到過了,遍歷結束。
深度優先遍歷和廣度優先遍歷的位移不一樣是使用到了棧而不是隊列來管理遍歷。
最小生成樹是邊的權重總和和小於或等於同一個圖中其餘任何一棵生成樹的權重總和。
- 非連通的無向圖,不存在最小生成樹
- 權重不必定和距離成正比
- 權重多是0或負數
- 若存在相等的權重,那麼最小生成樹可能不惟一
- Prim算法:
方法二:Dijkstra算法
以0頂點爲開始位置,0頂點到1頂點的權重爲3,到2頂點的權重爲max,到3頂點的權重爲4,到4頂點的權重爲5。
選取權重最小的頂點爲1頂點,由於全部的權重都爲正,那麼0頂點到1頂點的最短距離就是3,再以1頂點開始,1頂點能夠到4頂點和2頂點,到4頂點的位置是3+1=4<5,即0頂點到4頂點的最短距離是經過1頂點的,再看到2頂點的位置是3+10=13<max,即0頂點直接到2頂點的距離爲max,可是經過1頂點能夠縮短爲13。
扣除0頂點和1頂點已經肯定好了最短距離以後,選取最短的權重爲0頂點到3頂點爲4。
以3頂點開始,3頂點能夠到2頂點,到2頂點的位置是4+7=11<13,即0頂點到2頂點的最短距離,從max到經過1頂點再到經過3頂點到2頂點。
扣除0頂點、1頂點和3頂點,已經肯定好了最短距離以後,選取最小的權重爲0頂點到1頂點再到4頂點爲4,4頂點能夠到1頂點、3頂點和2頂點,可是3頂點和1頂點已經肯定好了,只剩2頂點,那麼到2頂點的位置爲3+1+6=10<11,即到2頂點的最短距離改成經過1頂點、4頂點最後到2頂點。
最後扣除0頂點、1頂點、3頂點和4頂點,只剩下2頂點,距離只剩10,因此到2頂點的最短距離爲10,結束算法。
鄰接列表
- 在鄰接表的表示中,無向圖的同一條邊在鄰接表中存儲的兩次。若是想要知道頂點的讀,只須要求出所對應鏈表的結點個數便可。
- 有向圖中每條邊在鄰接表中只出現一此,求頂點的出度只須要遍歷所對應鏈表便可。求出度則須要遍歷其餘頂點的鏈表。
無向圖的鄰接列表:
有向圖的鄰接列表:
鄰接矩陣
- 在鄰接矩陣表示中,無向圖的鄰接矩陣是對稱的。矩陣中第 i 行或 第 i 列有效元素個數之和就是頂點的度。
- 在有向圖中 第 i 行有效元素個數之和是頂點的出度,第 i 列有效元素個數之和是頂點的入度。
無向圖的鄰接矩陣:
有向圖的鄰接矩陣:
- 鄰接矩陣與鄰接表優缺點:鄰接矩陣的優勢是能夠快速判斷兩個頂點之間是否存在邊,能夠快速添加邊或者刪除邊。而其缺點是若是頂點之間的邊比較少,會比較浪費空間。由於是一個 n∗nn∗n 的矩陣。而鄰接表的優勢是節省空間,只存儲實際存在的邊。其缺點是關注頂點的度時,就可能須要遍歷一個鏈表。還有一個缺點是,對於無向圖,若是須要刪除一條邊,就須要在兩個鏈表上查找並刪除。
邊集數組
- 邊集數組由兩個一維數組構成:一個存儲頂點信息; 一個存儲邊的信息,這個邊數組每一個數據元素由一條邊的起點下標(begin)、終點下標(end)、和權(weight)組成。
- 邊集數組關注的是邊的集合,在邊集數組中要查找一個頂點的度須要掃描整個邊數組,效率並不高。所以它更適合對邊依次進行處理的操做,而不適合對頂點相關的操做。
無向圖的邊集數組:
有向圖的邊集數組:
十字鏈表
- 十字鏈表是爲了便於求得圖中頂點的度(出度和入度)而提出來的。用十字鏈表來存儲有向圖,能夠達到高效的存取效果。它是綜合鄰接表和逆鄰接表形式的一種鏈式存儲結構。
綠色鏈表表示以結點A爲弧頭的弧組成的鏈表。黃色鏈表表示以結點A爲弧尾的弧組成的鏈表。
鄰接多重表
- 鄰接多重表主要用於存儲無向圖。若是用鄰接表存儲無向圖,每條邊的兩個邊結點分別在以該邊所依附的兩個頂點爲頭結點的鏈表中,這給圖的某些操做帶來不便。所以,在進行這一類操做的無向圖的問題中採用鄰接多重表做存儲結構更爲適宜。
問題1解決方案:無向圖的連通性在書上已經給出,字面理解就是任意兩點之間都存在一條路徑,而從編寫的角度就是一個頂點開始深度優先遍歷或是廣度優先遍歷得出的頂點數。而有向圖的是具備方向的,若是這樣的話咱們如何斷定有向圖的連通性?在查詢的過程當中,發現有向圖的連通性能夠分紅強連通圖、弱連通圖以及單向連通圖。書上的圖15.4的連通圖就是一個弱連通圖,若是是強連通圖則須要在此基礎上每兩點之間都添加一條與之方向相反的線。針對有向圖的測試連通性的方法能夠用到Tarjan算法
- 在有向圖中, 若對於任意兩個兩點之間, 都存在互逆的路徑,則稱此圖是強連通圖。即有向圖中,若對於任意兩個不一樣的頂點x和y,都存在從x到y以及從y到x的路徑,則稱G是強連通圖。
- 有向圖的全部的有向邊替換爲無向邊,所獲得的圖稱爲原圖的基圖。若是一個有向圖的基圖是連通圖,則有向圖是弱連通圖。
- 若是有向圖中,對於任意節點v1和v2,至少存在從v1到v2和從v2到v1的路徑中的一條,則原圖爲單向連通圖。
- 強連通圖、連通圖、單向連通圖三者之間的關係是,強連通圖必然是單向連通的,單向連通圖必然是弱連通圖。
- 在Tarjan算法中爲每一個節點i維護瞭如下幾個變量:
DFN[i]:深度優先搜索遍歷時節點i被搜索的次序。
low[i]:節點i可以回溯到的最先位於棧中的節點。
flag[i]:標記幾點i是否在棧中。- Tarjan算法的運行過程:
(1)首先就是按照深度優先搜索算法搜索的次序對圖中全部的節點進行搜索。
(2)在搜索過程當中,對於任意節點u和與其相連的節點v,根據節點v是否在棧中來進行不一樣的操做:
a.節點v不在棧中,即節點v尚未被訪問過,則繼續對v進行深度搜索。
b.節點v已經在棧中,即已經被訪問過,則判斷節點v的DFN值和節點u的low值的大小來更新節點u的low值。若是節點v的 DFN值要小於節點u的low值,根據low值的定義(可以回溯到的最先的已經在棧中的節點),咱們須要用DFN值來更新u 的low值。
(3)在回溯過程當中,對於任意節點u用其子節點v(其實不能算是子節點,只是在深度遍歷的過程當中,v是在u以後緊挨着u的節點)的 low值來更新節點u的low值。由於節點v可以回溯到的已經在棧中的節點,節點u也必定可以回溯到。由於存在從u到v的直接路徑,因此v可以到的節點u也必定可以到。
(4)對於一個連通圖,咱們很容易想到,在該連通圖中有且僅有一個節點u的DFN值和low值相等。該節點必定是在深度遍歷的過程當中,該連通圖中第一個被訪問過的節點,由於它的DFN值和low值最小,不會被該連通圖中的其餘節點所影響。
public void DFSRecursion() { boolean[] visited = new boolean[numVertices]; for (int i = 0; i < numVertices; i++) visited[i] = false; for(int i=0;i<numVertices;i++) { if (!visited[i]) { depthFirstSearch(visited,i); } } } private void depthFirstSearch(boolean[] isVisited,int i) { System.out.print(vertices[i]+" "); isVisited[i]=true; int w=getFirstNeighbor(i); while (w!=-1) { if (!isVisited[w]) { depthFirstSearch(isVisited,w); } w=getNextNeighbor(i, w); } } public void broadFirstSearch() { boolean[] visited = new boolean[numVertices]; for (int i = 0; i < numVertices; i++) visited[i] = false; for(int i=0;i< numVertices;i++) { if(!visited[i]) { broadFirstSearch(visited, i); } } } private void broadFirstSearch(boolean[] isVisited,int i) { int u,w; LinkedList queue=new LinkedList(); System.out.print(vertices[i]+" "); isVisited[i]=true; queue.addLast(vertices[i]); while (!queue.isEmpty()) { u=((Integer)queue.removeFirst()).intValue(); w=getFirstNeighbor(u); while(w!=-1) { if(!isVisited[w]) { System.out.print(vertices[w]+" "); isVisited[w]=true; queue.addLast(w); } w=getNextNeighbor(u, w); } } }
問題2的解決方案:根據題目要求容許客戶輸入兩個城市以及兩個城市之間的價格,這就相似加權圖,根據GraphADT接口以及實現的添加頂點和添加邊的方法都沒有涉及到權重(這裏的權重爲兩個城市之間的價格),而無向圖實現的是肯定兩個頂點之間是否有邊,用一個二維布爾型數組存儲對應頂點是否有邊,咱們的想法是能夠將是否有邊來改成數字,用票價來實現是否有聯繫。那麼問題又來了,沒有聯繫的如何來界定?定義爲最大仍是最小?若是定義爲最小爲零的話,咱們的選擇就要在排除零的前提下進行Dijkstra算法,可是若是咱們將無聯繫的看做是無窮大的狀況下,就會產生不同的效果,在選取權重較小即最便宜路徑的時候就會很方便,並且事實上提供的算法也是要將無聯繫的設爲最大,就如同教材內容總結部分敘述的算法同樣。可是,不管是無窮大仍是無窮小可能是用什麼表示?和數序符號同樣麼?API提供瞭解決辦法(侯澤洋同窗提供):Double.POSITIVE_INFINITY
表示的是正無窮大值,Double.NEGATIVE_INFINITY
表示的是負無窮大值。這樣就能夠實現了,可是Dijkstra算法如何書寫呢?又是一個問題好喪...在網上查了幾個,沒有適用成功因此經過看侯澤洋的博客找到了一個不錯的博客,用他的算法來實現的具體代碼,並且侯澤洋的代碼層次設計的很是好,實現的也乾淨利落,很強大,是我值得學習的。此外,最短路徑就能夠用到Graph內的shortestPathLength
方法和iteratorShortestPath
實現,我在此基礎上經過返回最短路徑的數值,若是是1的話就會存在直達列車,大於1的話就會是不可直達,在同經過遍歷最短路徑的迭代方法進行輸出最短路徑就好。
錯題已經在上週博客寫過...
本週結對學習狀況
20172314方藝雯
20172323王禹涵
結對學習內容:圖和一堆算法
第十五章的圖感受和紅黑樹同樣,要構造很亂的數據結構來實現,此外還有一堆算法能夠優化圖的操做(算法做用很好,可是難於理解,有的思路就難理解)。好在有些代碼給出了,可是書上的遍歷代碼就沒給全,致使始終讀不懂一個方法具體幹嗎的,具體實現的策略也不一樣於以前的數據結構。要保持好的心態來學習代碼,切莫急功近利...
代碼行數(新增/累積) | 博客量(新增/累積) | 學習時間(新增/累積) | 重要成長 | |
---|---|---|---|---|
目標 | 5000行 | 30篇 | 400小時 | |
第一週 | 0/0 | 1/1 | 15/15 | |
第二週 | 703/703 | 1/2 | 20/35 | |
第三週 | 762/1465 | 1/3 | 20/55 | |
第四周 | 2073/3538 | 1/4 | 40/95 | |
第五週 | 981/4519 | 2/6 | 40/135 | |
第六週 | 1088/5607 | 2/8 | 50/185 | |
第七週 | 1203/6810 | 1/9 | 50/235 | |
第八週 | 2264/9074 | 2/11 | 50/285 | |
第九周 | 2045/11119 | 1/12 | 50/335 |