這一章又要學習一個新的體系了——圖html
接下來的例子都是使用這兩個圖
java
一些共同的基礎概念:git
自循環 :連通一個頂點及其自身的邊稱爲自循環或環, 例如,邊(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)
有向樹是種指定了個元素做爲樹根的有向圖,還有以下屬性:
不存在其餘頂點到樹根的鏈接。
每一個非樹根元素剛好有一個鏈接。
樹根到每一個其餘頂點都有一條路徑。
網絡
(這是一個網絡,該網絡描繪了在美國城市之間的連通性和飛機票價。這個加權圖(或網絡)能夠用於肯定從個城市到另外一個城市的最便宜行程。)
在這裏咱們學習的算法包括:各類遍歷算法(這些遍歷相似於探討過的樹遍歷),尋找最短路徑的算法,尋找網絡中最低代價路徑的算法,回答一些簡單圖相關問題(例如,圖是不是連通的,圖中兩個頂點間的最短路徑是什麼,等等)的算法。
遍歷
課本中用一個隊列和一個無序列表來構造圖的廣度優先遍歷。咱們將使用隊列(traversalQueue)來管理遍歷,使用無序列表(resultList) 來構造出結果。
用一個實例來講明:
(1)往traversalQucue中添加9,而且把它標記爲visited.
(2)從traversalQucuc中取出9.
(3)往resultList中添加9.
(4)往traversalQucue中添加六、7和8,網時把它們逐 標記爲visited.
(5)從traversalQueuc中取出6。
(6)往resultList中添加6。
(7)往raversalQucue中添加三、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開始的以下頂點: 九、六、七、八、三、四、五、1和2。
圖的深度優先遍歷構造使用了一樣的邏輯,只不過在深度優先遍歷中用traversalStack代替了traversalQueue可是, 算法中還有一處不一樣:在頂點還沒有添加到resultList以前,咱們並不想標記該項點爲visited.
測試連通性
最小生成樹
生成樹(spanningtree)是一棵含有圖中全部頂點和部分邊(但可能不是全部邊)的樹。
最小生成樹(minimum spanningtree, MST)是加權圖的最小生成樹,它是一棵生成樹, 其邊的權重總和小於或等於同一個圖中其餘任何一棵生成樹的權重總和。
算法:任意選取個起始頂點(無論是哪個), 並將它添加到最小生成樹中。而後,將全部含該起始頂點的邊按照權重次序添加到minheap (最小堆)中。(若是處理的是有向網絡,則只會添加那些以這個特定頂點爲起點的邊。)接着從minheap中取出最小邊,並將這條邊和那個新頂點添加到最小生成樹中。下一步,咱們往minbeap中添加全部含該新頂點的另外一頂點尚不在最小生成樹中的邊。繼續這過程,直到最小生成樹含有原始圖中的全部頂點(或minheap爲空)時結束。
斷定最短路徑
鄰接列表:對圖結點來講,因爲每一個結點能夠有多達n-1條邊與其餘結點相連,所以最好用種相似於鏈表的動態結點來存儲每一個結點帶有的邊。這種鏈表稱爲鄰接列表(adjacency is)。對網絡或加權圖而言,每條邊會存儲成一個含權重的三元組。對無向圖而言,邊(A,B)會同時出如今頂點A和頂點B的鄰接列表中。
鄰接矩陣:鄰接矩陣(adjacency matrix) 是一個二維數組。在鄰接矩陣中,這個二維數組中的每一個單元都表示了圖中兩個頂點的交接狀況。對矩陣中的任何單元(row, colum)而言,當且僅當圖中存在(Vrow,Vcolum)時,這個單元才爲true。因爲無向圖中的邊是雙向的,所以,若是(A, B)是圖中的一條邊,那麼(B,A)一樣也是圖中的一條邊。
、
(注意,這個矩陣是對稱的,即該矩陣對角線的一側是另外一 側的鏡像。 其緣由就在於它所表示的是個無向圖。 對無向圖來講,沒有必要表示整個矩陣,只需給出矩陣對角線的一側(或另外一側)便可。)
問題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解決方案:最近的的調試老是出現這樣的問題,看樣子是要解決一下了
(我以前還覺得是當初的那個代碼寫的有問題,如今才發現看樣子是有哪裏出問題了,否則之後都調試不了了)
其實個人編寫思路是和馨雨同窗同樣的,和她同樣在寫添加邊的過程當中出現了越界的問題,我思考了好久都沒有想出是爲何(我以爲本身的思路沒有問題TAT),而後就去參考了一些同窗們的寫法,最後屈服了,換了一種寫法。
「由於堆是二叉搜索樹,因此只有一個正確的位置用於插入新節點,而且若是級別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 | |
第九周 | 2444/12850 | 1/13 | 23/232 |
計劃學習時間:25小時
實際學習時間:23小時