1. 程式人生 > >20172310 2017-2018《程式設計與資料結構》(下)第九周學習總結

20172310 2017-2018《程式設計與資料結構》(下)第九周學習總結

20172310 2017-2018《程式設計與資料結構》(下)第九周學習總結

教材學習內容總結

這一章又要學習一個新的體系了——圖

無向圖、有向圖和網路

接下來的例子都是使用這兩個圖

一些共同的基礎概念

  • 頂點:結點,一般用名字和標籤來表示,如A、B等。
  • 邊:節點之間的連線,用結點對來表示,如A、B頂點間的一條邊(A,B)。
  • 鄰接:如果圖中的兩個頂點之間有一條連通邊,則稱這兩個頂點是鄰接的,如圖中AB鄰接,A、D不鄰接。
  • 自迴圈 :連通一個頂點及其自身的邊稱為自迴圈或環, 例如,邊(A,A) 表示的是連線A到自身的一個環。

  • 路徑: 無向圖中是指圖中的一系列邊,每條邊連通兩個頂點。例如,圖1,ABD就是一條從A到D的路徑。有向圖中的路徑是圖中連通兩個頂點的有向邊序列。

無向圖中的路徑是雙向的。例如,ABD確實是從A到D的路徑,但由於這些邊並沒有方向,因此反過來,DBA又是從D到A的路徑。

  • 路徑的長度:是該路徑中邊的條數(或者是頂點數減去1)。如,從A到D的路徑長度是2。注意, 這裡對路徑長度的定義等同於討論樹時用到的路徑長度定義,因為, 樹也是圖的一種。

  • 環路:是一種首頂點和末頂點相同且沒有重邊的路徑。在圖1中,我們稱路徑ABCA是一條環路。沒有環路的圖稱為無環的。

  • 連通:在無向圖中,如果其中的任意兩個頂點之間都存在一條路徑,則認為這個無向圖是連通的(connected),連通有向圖的定義聽起來和連通無向圖的定義一樣。如果有向圖中的任意兩個頂點之間都存在一條路徑, 則認為該有向圖是連通的。但是記住,對有向圖和無向圖的路徑定義是不同的。


無向圖

  • 定義:無向圖(undirected graph)是一種邊為無序結點對的圖。記作(A, B)的邊就意味著A與B之間有一條從兩個 方向都可以遊歷的連線 ,(A,B) 和記作(B,A) 的含義是完全樣的。

  • 如果無向圖擁有最大數目的連通頂點的邊,則認為這個無向圖是完全的。也就是說,對於第一個頂點,需要(n一1)條邊將其與其他頂點連通。對於第2個頂點,只需(n-2)條邊(除去與第一個頂點相連的邊)。第三個頂點需要 (n-3)條邊。 所以,對有n個頂點的無向圖,要使該圖為完全的,要求有n(n- 1)/2條邊。注意,這裡假設其中沒有邊是自迴圈的。

  • 無向樹(undirected tree)是一種連通的無環無向圖,其中一個元素被指定為樹根。


有向圖

  • 有向圖(directed graph)有時也稱為雙向圖(digraph),它是一種邊為有序頂點對的圖。這意味著邊(A,B) 和邊(B, A)在有向圖中是不同的有向邊。

  • 如果有向圖中沒有環路,且有一條從A到B的邊,則可以把頂點A安排在頂點B之前。這種排列得到的頂點次序稱為拓撲序(lopological order)

  • 有向樹是種指定了個元素作為樹根的有向圖,還有如下屬性:

    • 不存在其他頂點到樹根的連線。

    • 每個非樹根元素恰好有一個連線。

    • 樹根到每個其他頂點都有一條路徑。


網路

  • 網路(network),或稱為加權圖(weighted graph),是一種每 條邊都帶有權重或代價的圖。網路可以是無向的,也可以是有向的,滿足有向圖或無向圖的性質。通常,我們用一個三元組來表示每條邊。,這個三元組中包括起始頂點、 終止項點和權重。記住,對無向網路來說,起始頂點與終止頂點可以互換。但對有向圖來說,必須包含每個有向連線的三元組。

(這是一個網路,該網路描繪了在美國城市之間的連通性和飛機票價。這個加權圖(或網路)可以用於確定從個城市到另一個城市的最便宜行程。)

  • 加權圖中的路徑權重是該路徑中各條邊權重的和。

圖的常用演算法

在這裡我們學習的演算法包括:各種遍歷演算法(這些遍歷類似於探討過的樹遍歷),尋找最短路徑的演算法,尋找網路中最低代價路徑的演算法,回答一些簡單圖相關問題(例如,圖是否是連通的,圖中兩個頂點間的最短路徑是什麼,等等)的演算法。


遍歷

  • 圖的遍歷分成兩類(注意,圖中不存在根結點,因此圖的遍歷可以從其中的任頂點開始):
    • 廣度優先遍歷(breadth-first traversal),類似於樹的層次遍歷

課本中用一個佇列和一個無序列表來構造圖的廣度優先遍歷。我們將使用佇列(traversalQueue)來管理遍歷,使用無序列表(resultList) 來構造出結果。
用一個例項來說明:


(1)往traversalQucue中新增9,並且把它標記為visited.
(2)從traversalQucuc中取出9.
(3)往resultList中新增9.
(4)往traversalQucue中新增6、7和8,網時把它們逐 標記為visited.
(5)從traversalQueuc中取出6。
(6)往resultList中新增6。
(7)往raversalQucue中新增3、4,同時把這兩個頂點標記為vsited.
(8)從traversalQueue中取出7,並將它新增到resutList中。
(9)往traversalQueue中新增5,並且把它標記為visited.
(10)從traversalQueure中取出8,並將它新增到resutList中(這時不再往traversal.Qucue中新增任何新的頂點,因為頂點8再也沒有尚未訪問過的鄰居了).
(11)從traversalQueuc中取出3,並將它新增到resultList中。
(12)往traversalQueue中新增1,並且把它標記為visited.
(13)從traversalQueue中取出4,並將它新增到resultList中。
(14)往traversalQueue中新增2,並且把它標記為visited.
(15)從traversalQueue中取出5,並將它新增到resultList中(由於再也沒有未訪問過的鄰居,因此不需再往traversalQueue中新增頂點)。
(16)從traversalQueue中取出1,並將它新增到resultList中(由於再也沒有未訪問過的鄰居,因此不需再往traversalQueue中新增頂點)。
(17)從traversalQucue中取出2,並將它新增到resultList中。
這樣,resultList 就以廣度優先次序存放了從頂點9開始的如下頂點: 9、6、7、8、3、4、5、1和2。

  • 深度優先遍歷(depth-first traversal),類似於樹的前序遍歷

圖的深度優先遍歷構造使用了同樣的邏輯,只不過在深度優先遍歷中用traversalStack代替了traversalQueue但是, 演算法中還有一處不同:在頂點尚未新增到resultList之前,我們並不想標記該項點為visited.


測試連通性

  • 關鍵思想:不論哪個為起始頂點,當且僅當廣度優先遍歷中的頂點數目等於圖中的頂點數目時,該圖才是連通的。

最小生成樹

  • 生成樹(spanningtree)是一棵含有圖中所有頂點和部分邊(但可能不是所有邊)的樹。

  • 最小生成樹(minimum spanningtree, MST)是加權圖的最小生成樹,它是一棵生成樹, 其邊的權重總和小於或等於同一個圖中其他任何一棵生成樹的權重總和。

  • 演算法:任意選取個起始頂點(不管是哪一個), 並將它新增到最小生成樹中。然後,將所有含該起始頂點的邊按照權重次序新增到minheap (最小堆)中。(如果處理的是有向網路,則只會新增那些以這個特定頂點為起點的邊。)接著從minheap中取出最小邊,並將這條邊和那個新頂點新增到最小生成樹中。下一步,我們往minbeap中新增所有含該新頂點的另一頂點尚不在最小生成樹中的邊。繼續這過程,直到最小生成樹含有原始圖中的所有頂點(或minheap為空)時結束。


判定最短路徑

  • 判定圖的“最短”路徑有兩種情況。
    • 第一種, 判定起始頂點與目標頂點之間的字面意義上的最短路徑,也就是兩個頂點之間的最小邊數。可以將廣度優先遍歷演算法轉變成尋找最短路徑的演算法,只需在遍歷期間再對每個頂點儲存另兩個資訊即可:從起始頂點到本頂點的路徑長度,以及路徑中作為本項點前驅的那個頂點。接著要修改迴圈,使得當抵達目標頂點時迴圈將終止。最短路徑的路徑長度就是從起始頂點到目標頂點前驅的路徑長度再加1;如果要輸出這條最短路徑上的頂點,只需沿著前驅鏈回溯即可。
    • 判定最短路徑的第二種情況是,尋找加權圖的最便宜路徑。用一個minheap或優先佇列來儲存頂點,基於總權重(從起始頂點到本頂點的權重和)來衡量頂點對,這樣我們總是能優先沿著最便宜的路徑來遊歷圖。對每個頂點都必須儲存該頂點的標籤,(迄今為止)從起始頂點到本頂點的最便宜路徑的權重,路徑上本頂點的前驅。在minheap中將儲存頂點、對每條已經遇到但尚未遊歷的候選路徑來權衡頂點對。從minheap取出頂點的時候,會權衡取自minheap 的頂點對:如果遇到一個頂點的權重小於目前本頂點中已儲存的權重,則會更新路徑的代價。

圖的實現策略

  • 鄰接列表:對圖結點來說,由於每個結點可以有多達n-1條邊與其他結點相連,因此最好用種類似於連結串列的動態結點來儲存每個結點帶有的邊。這種連結串列稱為鄰接列表(adjacency is)。對網路或加權圖而言,每條邊會儲存成一個含權重的三元組。對無向圖而言,邊(A,B)會同時出現在頂點A和頂點B的鄰接列表中。

  • 鄰接矩陣:鄰接矩陣(adjacency matrix) 是一個二維陣列。在鄰接矩陣中,這個二維陣列中的每個單元都表示了圖中兩個頂點的交接情況。對矩陣中的任何單元(row, colum)而言,當且僅當圖中存在(Vrow,Vcolum)時,這個單元才為true。由於無向圖中的邊是雙向的,因此,如果(A, B)是圖中的一條邊,那麼(B,A)同樣也是圖中的一條邊。


(注意,這個矩陣是對稱的,即該矩陣對角線的一側是另一 側的映象。 其原因就在於它所表示的是個無向圖。 對無向圖來說,沒有必要表示整個矩陣,只需給出矩陣對角線的一側(或另一側)即可。)

用鄰接矩陣實現無向圖

點選檢視程式碼實現

教材學習中的問題和解決過程

  • 問題1:教材中講述遍歷的時候只詳細介紹了廣度優先遍歷,和給出了深度優先遍歷的程式碼,那深度優先遍歷的情況是怎樣的呢?而且遍歷都是以無向圖來說明的,但是如果有向圖又該是什麼樣的情況呢?

  • 問題1解決方案:
  • 廣度優先遍歷在上面的教材總結中已經詳細的介紹過一下流程
  • 深度優先遍歷的情況

有向圖的遍歷其實就是看圖是否是連通的,看網上的這幅圖

他的深度優先遍歷情況就應該是:

第1步:訪問A。
第2步:訪問B。
在訪問了A之後,接下來應該訪問的是A的出邊的另一個頂點,即頂點B。
第3步:訪問C。
在訪問了B之後,接下來應該訪問的是B的出邊的另一個頂點,即頂點C,E,F。在本文實現的圖中,頂點ABCDEFG按照順序儲存,因此先訪問C。
第4步:訪問E。
接下來訪問C的出邊的另一個頂點,即頂點E。
第5步:訪問D。
接下來訪問E的出邊的另一個頂點,即頂點B,D。頂點B已經被訪問過,因此訪問頂點D。
第6步:訪問F。
接下應該回溯"訪問A的出邊的另一個頂點F"。
第7步:訪問G。
因此訪問順序是:A -> B -> C -> E -> D -> F -> G

他的廣度優先遍歷情況就應該是:

第1步:訪問A。
第2步:訪問B。
第3步:依次訪問C,E,F。
在訪問了B之後,接下來訪問B的出邊的另一個頂點,即C,E,F。前面已經說過,在本文實現中,頂點ABCDEFG按照順序儲存的,因此會先訪問C,再依次訪問E,F。
第4步:依次訪問D,G。
在訪問完C,E,F之後,再依次訪問它們的出邊的另一個頂點。還是按照C,E,F的順序訪問,C的已經全部訪問過了,那麼就只剩下E,F;先訪問E的鄰接點D,再訪問F的鄰接點G。
因此訪問順序是:A -> B -> C -> E -> F -> D -> G

大家也可以參考一下這篇資料圖的遍歷之 深度優先搜尋和廣度優先搜尋

程式碼除錯中的問題和解決過程

  • 問題1:編寫pp15.1即用鄰接列表實現無向圖最難的在我看來是遍歷的書寫。

  • 問題1解決方案:之前我找百度,看見裡面的寫法很多都是直接呼叫了Java類庫中的ArrayList類,我也找同學幫助看了看他們編寫的思路,也有用了類庫的那樣他們的遍歷方法可以繼續使用原來的不用重新編寫,但是我覺得那個本質上使用的只是陣列套陣列,並沒有連結串列的影子。我還看到了另一種寫法,就是使用自己當初編寫的指標類,像是這樣的。

所以我想直接使用我們當初編寫的LinearNode類,這樣就像我們之前學習到的雜湊函式的編寫方法,但是這時的遍歷方法就要改寫。
對原來的程式碼進行了改寫程式碼連結

  • 問題2:

  • 問題2解決方案:最近的的除錯總是出現這樣的問題,看樣子是要解決一下了

(我之前還以為是當初的那個程式碼寫的有問題,現在才發現看樣子是有哪裡出問題了,不然以後都除錯不了了)

程式碼託管

上週考試錯題總結

  • 錯題1及原因,理解情況

“因為堆是二叉搜尋樹,所以只有一個正確的位置用於插入新節點,並且如果級別h已滿,該位置可以是從級別h左側的下一個開啟位置或在級別h+1左側的第一個開啟位置。”
這道題會錯是因為自己只注意到了後面的講述是對的,沒看到最關鍵的前提 堆是一棵完全樹,才有後面的內容。

結對及互評

點評:

  • 本週結對學習情況
  • 部落格和程式碼中值得學習的或問題:

點評過的同學部落格和程式碼

其他(感悟、思考等,可選)

又是緊張的一週學習,其實這周的作業是上次作業都還沒完成的情況下就釋出了的,但還是時間緊張呀。這周其實課本
上給出的程式碼不算多,所以程式碼作業就看起來挺多的。時光匆匆,貌似我們這一本課本也學習完了(其實還是沒有吃透(つД`)),
馬上又要開始魔鬼般的實驗作業了,希望我不會給我的小夥伴們拖後腿了。

學習進度條

程式碼行數(新增/累積) 部落格量(新增/累積) 學習時間(新增/累積)
目標 5000行 30篇 400小時
第一週 0/0 1/1 10/10
第二週 326/326 1/2 18/28
第三週 784/1110 1/3 25/53
第四周 2529/3638 2/5 37/90
第五週 1254/4892 2/7 20/110
第六週 1403/6295 2/9 32/142
第七週 1361/7656 1/10 35/177
第八週 2750/10406 2/12 32/209
第九周 1/13 23/232
  • 計劃學習時間:25小時

  • 實際學習時間:23小時

參考資料