20172313 2018-2019-1 《程序設計與數據結構》第九周學習總結
教材學習內容總結
無向圖
- 無向圖:與樹相似,圖也由結點和這些結點之間的鏈接構成。這些結點是頂點,而結點之間的連接是邊。無向圖是一種邊爲無序結點對的圖。因而,記作(A,B)的邊就意味着A與B之間有一條從兩個方向均可以遊歷的鏈接。邊記做(A,B)和記做(B,A)的含義是徹底同樣的。
- 徹底圖:若是一個無向圖含有最多條邊,那麼它爲徹底圖。例如:
- 徹底圖有n(n-1)條邊。(此時假設沒有邊自循環。)
- 鏈接圖中兩個頂點的邊的序列,能夠由多條邊組成。(圖中的路徑是雙向的。)
- 路徑長度:路徑中所含邊的數目(頂點個數減1)。
- 連通圖:無向圖中任意兩個頂點之間都存在一條路徑。(徹底圖必定是連通圖,連通圖不必定是徹底圖。)

- 環路:一種首頂點和末頂點相同且沒有重邊的路徑。

- 無環圖:沒有環路的圖。
- 無向圖是一種連通的無環無向圖,其中一個元素被指定爲樹根。
有向圖
- 有向圖:又稱雙向圖,是一種邊爲有序頂點對的圖。(邊(A,B)和邊(B,A)方向不一樣)
- 有向路徑:鏈接兩個頂點有向邊的序列。
- 注意:注意連通圖的有向圖和無向圖之間的不一樣:
- 上圖第一個圖是連通的,第二個圖並非連通的,由於沒有任何路徑能從其餘頂點遊歷到頂點A。
- 拓撲序:有向圖中沒有環路,且有一條從A到B的邊,則能夠吧頂點A安排在頂點B以前,這種排列獲得的頂點次序。
- 有向樹是一個有向圖,其中指定一個元素爲根,則具備下列特性:
- 不存在其餘頂點到樹根的鏈接。
- 每一個非樹根元素剛好有一個鏈接。
- 樹根到每一個其餘頂點都有一條路徑。
網絡
- 網絡:又稱加權圖,每條邊都對應一個權值(數據信息)的圖,能夠是有向的也能夠是無向的。(城市之間的航線、票價等)
- 網絡的邊由起始頂點、終止定點和權重構成的三元組來表示。
經常使用的圖算法
- 深度優先遍歷:和樹的先序遍歷比較相似。假設初始狀態是圖中全部頂點均未被訪問,則從某個頂點v出發,首先訪問該頂點,而後依次從它的各個未被訪問的鄰接點出發深度優先搜索遍歷圖,直至圖中全部和v有路徑相通的頂點都被訪問到。 若此時尚有其餘頂點未被訪問到,則另選一個未被訪問的頂點做起始點,重複上述過程,直至圖中全部頂點都被訪問到爲止。
- 無向圖示例以下:
- ①訪問A。
- ②訪問(A的鄰接點)C。在第1步訪問A以後,接下來應該訪問的是A的鄰接點,即"C,D,F"中的一個。但在本文的實現中,頂點ABCDEFG是按照順序存儲,C在"D和F"的前面,所以,先訪問C。
- ③訪問(C的鄰接點)B。
在第2步訪問C以後,接下來應該訪問C的鄰接點,即"B和D"中一個(A已經被訪問過,就不算在內)。而因爲B在D以前,先訪問B。
- ④訪問(C的鄰接點)D。在第3步訪問了C的鄰接點B以後,B沒有未被訪問的鄰接點;所以,返回到訪問C的另外一個鄰接點D。
- ⑤訪問(A的鄰接點)F。前面已經訪問了A,而且訪問完了"A的鄰接點B的全部鄰接點(包括遞歸的鄰接點在內)";所以,此時返回到訪問A的另外一個鄰接點F。
- ⑥訪問(F的鄰接點)G。
- ⑦訪問(G的鄰接點)E。
- 所以訪問順序是:A -> C -> B -> D -> F -> G -> E
- 有向圖示例以下:
- ①訪問A。
- ②訪問B。在訪問了A以後,接下來應該訪問的是A的出邊的另外一個頂點,即頂點B。
- ③訪問C。在訪問了B以後,接下來應該訪問的是B的出邊的另外一個頂點,即頂點C,E,F。在本文實現的圖中,頂點ABCDEFG按照順序存儲,所以先訪問C。
- ④訪問E。接下來訪問C的出邊的另外一個頂點,即頂點E。
- ⑤訪問D。接下來訪問E的出邊的另外一個頂點,即頂點B,D。頂點B已經被訪問過,所以訪問頂點D。
- ⑥訪問F。接下應該回溯"訪問A的出邊的另外一個頂點F"。
- ⑦訪問G。
- 所以訪問順序是:A -> B -> C -> E -> D -> F -> G
- 廣度優先遍歷
- 又稱爲"寬度優先搜索"或"橫向優先搜索",簡稱BFS。從圖中某頂點v出發,在訪問了v以後依次訪問v的各個不曾訪問過的鄰接點,而後分別從這些鄰接點出發依次訪問它們的鄰接點,並使得「先被訪問的頂點的鄰接點先於後被訪問的頂點的鄰接點被訪問,直至圖中全部已被訪問的頂點的鄰接點都被訪問到。若是此時圖中尚有頂點未被訪問,則須要另選一個不曾被訪問過的頂點做爲新的起始點,重複上述過程,直至圖中全部頂點都被訪問到爲止。換句話說,廣度優先搜索遍歷圖的過程是以v爲起點,由近至遠,依次訪問和v有路徑相通且路徑長度爲1,2...的頂點。
- 無向圖示例以下:
- ①訪問A。
- ②依次訪問C,D,F。在訪問了A以後,接下來訪問A的鄰接點。前面已經說過,在本文實現中,頂點ABCDEFG按照順序存儲的,C在"D和F"的前面,所以,先訪問C。再訪問完C以後,再依次訪問D,F。
- ③依次訪問B,G。在第2步訪問完C,D,F以後,再依次訪問它們的鄰接點。首先訪問C的鄰接點B,再訪問F的鄰接點G。
- ④訪問E。在第3步訪問完B,G以後,再依次訪問它們的鄰接點。只有G有鄰接點E,所以訪問G的鄰接點E。
- 所以訪問順序是:A -> C -> D -> F -> B -> G -> E
- 有向圖示例以下:
- ①訪問A。
- ②訪問B。
- ③依次訪問C,E,F。在訪問了B以後,接下來訪問B的出邊的另外一個頂點,即C,E,F。前面已經說過,在本文實現中,頂點ABCDEFG按照順序存儲的,所以會先訪問C,再依次訪問E,F。
- ④依次訪問D,G。在訪問完C,E,F以後,再依次訪問它們的出邊的另外一個頂點。仍是按照C,E,F的順序訪問,C的已經所有訪問過了,那麼就只剩下E,F;先訪問E的鄰接點D,再訪問F的鄰接點G。
- 所以訪問順序是:A -> B -> C -> E -> F -> D -> G
- 測試連通性:從任意結點開始的廣度優先遍歷中獲得的頂點數等於圖中所含頂點數。
- 最小生成樹
- 生成樹:生成樹是一棵含有圖中全部頂點和部分邊(但可能不是全部邊)的樹。
- 最小生成樹:所含邊權值之和小於其餘生成樹的邊的權值之和。計算最小生成樹通常有兩種算法:Kruskal和Prim算法
- Kruskal算法:此算法能夠稱爲「加邊法」,初始最小生成樹邊數爲0,每迭代一次就選擇一條知足條件的最小代價邊,加入到最小生成樹的邊集合裏。具體操做以下:
- ① 把圖中的全部邊按代價從小到大排序;
- ② 把圖中的n個頂點當作獨立的n棵樹組成的森林;
- ③ 按權值從小到大選擇邊,所選的邊鏈接的兩個頂點ui,vi,應屬於兩顆不一樣的樹,則成爲最小生成樹的一條邊,並將這兩顆樹合併做爲一顆樹。
- ④重複③,直到全部頂點都在一顆樹內或者有n-1條邊爲止。

- Prim算法:此算法能夠稱爲「加點法」,每次迭代選擇代價最小的邊對應的點,加入到最小生成樹中。算法從某一個頂點s開始,直到最小生成樹含有原始圖的全部頂點時結束。具體操做以下:
- ①圖的全部頂點集合爲V;初始令集合u={s},v=V−u;
- ②在兩個集合u,v可以組成的邊中,選擇一條代價最小的邊(u0,v0),加入到最小生成樹中,並把v0併入到集合u中。
- ③重複上述步驟,直到最小生成樹有n-1條邊或者n個頂點爲止。

- 判斷最短路徑
- 第一種方法:斷定起始頂點和目標頂點之間是否存在最短路徑(兩個頂點之間邊數最少的路徑)。
- 第二種方法:Dijkstra算法:設G=(V,E)是一個帶權有向圖,把圖中頂點集合V分紅兩組,第一組爲已求出最短路徑的頂點集合(用S表示,初始時S中只有一個源點,之後每求得一條最短路徑 , 就將加入到集合S中,直到所有頂點都加入到S中,算法就結束了),第二組爲其他未肯定最短路徑的頂點集合(用U表示),按最短路徑長度的遞增次序依次把第二組的頂點加入S中。在加入的過程當中,總保持從源點v到S中各頂點的最短路徑長度不大於從源點v到U中任何頂點的最短路徑長度。此外,每一個頂點對應一個距離,S中的頂點的距離就是從v到此頂點的最短路徑長度,U中的頂點的距離,是從v到此頂點只包括S中的頂點爲中間頂點的當前最短路徑長度。具體操做以下;
- ①初始時,S只包含源點,即S={v},v的距離爲0。U包含除v外的其餘頂點,即:U={其他頂點},若v與U中頂點u有邊,則<u,v>正常有權值,若u不是v的出邊鄰接點,則<u,v>權值爲∞。
- ②從U中選取一個距離v最小的頂點k,把k,加入S中(該選定的距離就是v到k的最短路徑長度)。
- ③以k爲新考慮的中間點,修改U中各頂點的距離;若從源點v到頂點u的距離(通過頂點k)比原來距離(不通過頂點k)短,則修改頂點u的距離值,修改後的距離值的頂點k的距離加上邊上的權。
- ④重複步驟b和c直到全部頂點都包含在S中。
- 示例以下,求從頂點v1到其餘各個頂點的最短路徑

首先第一步,咱們先聲明一個dis數組,該數組初始化的值爲:

這時頂點集合S中只有一個元素:S={v1},既然是求V1頂點到其他各個頂點的最短路徑,那麼咱們就先找到V1可以直接到達的點,從圖中可知是V2和V3,咱們把dis[m]設爲從頂點d[0]處到V(m+1)的路徑查毒,同時把全部其餘(頂點不能直接到達的)頂點的路徑長度設爲無窮大。這時在數組中表示就爲dis[1]=12,dis[2]=6。由於目前離 v1頂點最近的是 v3頂點,而且這個圖全部的權重都是正數,因此不可能經過第三個頂點中轉,這時V1到V3的最短路程就是dis[2]值。將V3加入S中。
咱們如今已經肯定了一個點的最短路徑,下面咱們來看V3能到哪些點,從圖中看V3只能到V5且V1——V3——V5的距離爲17,顯而易見要比∞要小,因此數組更新以下。

此時V1和V3都已經存入S中,咱們又從除dis[2]和dis[0]外的其餘值中尋找最小值,此時dis[1]最小,同理,V1到V2的最短距離就是dis[1]的值,將V2加入U中,從圖中看V2能到V3和V4,前面分析過,V1到V3的距離是最短的,因此再也不看V2到V4,V1——V2——V4的距離爲15,顯而易見要比∞要小,因此數組更新以下。

此時V一、V二、V3都已經存入S中,咱們又從除dis[0]、dis[1]、dis[2]外的其餘值中尋找最小值,此時dis[3]最小,將V4加入U中,從圖中看V4只能到V5,有前面可知V1——V4的最短距離爲15,因此V1——V4——V5的距離爲16,比17小,因此對dis[4]進行替換
,數組更新以下

而後再次尋找最小值,將V5加入U中,計算完畢。
圖的實現策略
- 鄰接列表:將每一個頂點的鄰接點串成一個單鏈表。

- 鄰接矩陣: 邏輯結構分爲兩部分:Vexs[](存儲頂點)和Arcs[][](鄰接矩陣)集合。所以,用一個一維數組存放圖中全部頂點數據;用一個二維數組存放頂點間關係(邊或弧)的數據,這個二維數組稱爲鄰接矩陣。無向圖的鄰接矩陣示例以下:

教材學習中的問題和解決過程
- 問題1:在學習教材的時候,最小生成樹的方法中有這樣一行代碼「resultGraph[][] = Double.Positive_INFINITY」,不是很理解這裏的POSITIVE_INFINITY表明什麼意思。
- 問題1解決方案:咱們首先來思考這樣一個問題,在進行浮點數運算的時候,有時咱們會遇到除數爲0的狀況,那咱們該如何解決呢?因此引入了無限這個概念,POSITIVE_INFINITY正是能夠用來表明無限。示例以下
double i = Double.POSITIVE_INFINITY; // i表示爲無限大
public static final double POSITIVE_INFINITY = 1.0 / 0.0;
- 問題2:不是很理解求最短路徑時的Dijkstra算法。
- 問題2解決方案:前文有詳細敘述。
- 問題3:不是很理解求最小生成樹的Prim算法。
- 問題3解決方案:前文有詳細敘述。
代碼調試中的問題和解決過程
- 問題1:在作pp15_1的時候,對方法進行測試,陷入了死循環。

- 問題1解決方案:首先,我先要肯定是哪一個方法致使了死循環,因爲我調用了toString方法,但屏幕上並無出現任何數值,這就說明要麼是toString出現了問題,要麼在它以前就出現了錯誤。從前日後分析,把全部的操做都註釋掉,依次判斷,發現了一個奇怪的現象,當第一次調用addEdge方法的時候是不會出現死循環的,在第二次的時候纔會出現。雖然不知道具體在哪一個地方出現了問題,但能夠確定的確是addEdge出了毛病。用debug對代碼進行分析以後發現了問題,因爲鄰接列表的數據結構,利用列表中的元素的指針指向其餘元素的時候不能直接指向該列表中的元素,而是要用一個temp的其值進行儲存,再用指針指向它,這樣問題就得以解決了。


- 問題2:在作pp15_7計算帶權重的有向圖的最短路徑的時候沒法獲得指望值。
- 問題2解決方案:在輸入的時候我想得到A到C的權重和最小的路徑,卻獲得的值爲49。由於個人思路是把原先判斷兩個頂點之間是否有邊的adjMatrix的數組改爲了存放兩頂點之間邊權重的數組。若是兩頂點之間沒有邊,將邊的值設置爲-1,若是有邊但沒有存權重就把它設置爲無窮大。出現的值是49,多是由於在計算的時候加上了-1,因此我就想應該要在哪裏添加判斷條件。

我在上圖所示的地方加上了判斷條件,但結果並無發生改變。判斷條件是temp < dist[j],爲dis[]加限定條件只是限制了數組值的範圍,並不會致使出現49的狀況。還要對adjMaxtrix[][]進行限制。以下圖所示,問題就得以解決了
for (int j = 0; j < numVertices; j++) {
if (adjMatrix[k][j] != -1&&dist[j]!= -1) {
double temp = (adjMatrix[k][j] == Double.POSITIVE_INFINITY
? Double.POSITIVE_INFINITY : (min + adjMatrix[k][j]));
if (flag[j] == false && (temp < dist[j])) {
dist[j] = temp;
previous[j] = k;
}
}
}


上週考試錯題總結
這周沒有錯題哦~算法
結對及互評
- 博客中值得學習的或問題:
- 排版精美,對教材的總結細緻,善於發現問題,對於問題研究得很細緻,解答也很周全。
- 代碼中值得學習的或問題:
點評過的同窗博客和代碼
其餘(感悟、思考等,可選)
剛剛感受上週的內容簡單了一些,本章的內容又給了我一悶棍,本章的難度不只難在算法的理解上,還難在代碼的實現上,光是學習算法就花費了很多時間,關鍵代碼的理解也不容易。但這周學下來整體上說收穫仍是挺大的,這周的學習狀態也還算能夠,但願本身可以繼續保持這樣的狀態,繼續進步吧!數組
學習進度條
第一週 |
200/200 |
1/1 |
5/20 |
|
第二週 |
981/1181 |
1/2 |
15/20 |
|
第三週 |
1694/2875 |
1/3 |
15/35 |
|
第四周 |
3129/6004 |
1/4 |
15/50 |
|
第五週 |
1294/7298 |
1/5 |
15/65 |
|
第六週 |
1426/8724 |
1/6 |
20/85 |
|
第七週 |
2071/10795 |
1/7 |
20/105 |
|
第八週 |
3393/14188 |
1/8 |
20/125 |
|
第九周 |
3217/17045 |
1/9 |
20/145 |
|
計劃學習時間:20小時網絡
實際學習時間:20小時數據結構
參考資料