本週學習了第15章圖
圖和圖論銜接了數學和計算機科學的整個分支學科php
若是無向圖擁有最大數目的連通頂點的邊,則認爲這個無向圖是徹底的html
對有n個頂點的無向圖,要使該圖是徹底的,要求有n(n-1)/2條邊node
環路是一種首頂點和末頂點相同且沒有重邊的路徑。無向樹是一種連通的無環無向圖,其中一個元素被指定爲樹根。git
若是有向圖中沒有環路,且有一條從A到B的邊,則能夠把頂點A安排在頂點B以前,這樣排列獲得的頂點次序稱爲拓撲序。web
- 有向樹是一種指定了一個元素作爲樹根的有向圖,該圖含有如下性質
- 不存在其餘頂點到樹根的鏈接
- 每一個非樹根元素剛好有一個鏈接
- 樹根到每一個其餘頂點都有一條路徑
adjMatrix[index1][index2] = true, adjMartix[index2][index1] = true
。問題3解決方案:廣度優先遍歷根據書上的代碼已經很好理解了,從任意一個頂點開始,而後分別找該頂點相鄰的頂點。可是深度優先遍歷相似於前序遍歷,這種狀況下也是去找該頂點的左右頂點嗎?
查閱資料,有人很形象地把深度優先遍歷總結成了一句話「一路走到頭,不撞牆不回頭」,這個意思就是說,遍歷以一個未被訪問過的頂點做爲起始頂點,沿當前頂點的邊走到未訪問過的頂點,當沒有未訪問過的頂點時,則回到上一個頂點,繼續試探別的頂點,直到全部的頂點都被訪問過。算法
以上圖爲例,以v1爲起始頂點,首先走v2-->v4-->v8-->v5,而後沒有能夠訪問的頂點了,返回到v1,再按照一路走到底的規則,走v3-->v6-->v7,遍歷完成。編程
如今來看代碼數組
public Iterator iteratorBFS(T startVertex) { return iteratorBFS(getIndex(startVertex)); }//公有方法,一樣是找到起始頂點在頂點列表中的索引 private Iterator iteratorBFS(int startIndex) { Integer x; Queue<Integer> tq = new LinkedQueue<Integer>(); UnorderedListADT<T> rls = new ArrayUnoderedQueue<T>();//廣度優先遍歷須要一個隊列和一個無序列表 if (!indexIsValid(startIndex)) { return rls.iterator(); }//判斷索引值是否合法,若是不合法,返回一個rls的迭代,顯示出來就是空 boolean[] visited = new boolean[numVertices]; for (int i = 0; i < numVertices; i++) { visited[i] = false; }//建立一個存儲量爲圖的頂點數大小的布爾值數組,將數組內每個值都設置爲false,表示未訪問過 tq.enqueue(new Integer(startIndex));//將起始頂點的值存在隊列中 visited[startIndex] = true;//將起始頂點對應的在布爾值數組中的位置設爲true,表示已訪問 while (!tq.isEmpty()) { // 出隊列 x = tq.dequeue(); // 遍歷記錄表 rls.addToRear(vertices[x.intValue()]);//將每個已訪問的頂點值存入無序列表中 for (int i = 0; i < numVertices; i++) { if (adjMatrix[x.intValue()][i] && !visited[i]) {//這裏能夠用鄰接矩陣來表示它的相鄰頂點,由於若是兩個頂點是連通的的話,那麼adjMatrix[x.intValue()][i]將會爲true,因此這裏的intValue方法應該也是定位數字的索引值 tq.enqueue(new Integer(i)); visited[i] = true; }//將與該頂點相鄰且未訪問過的頂點按照以前的順序,先存入隊列中,而後將其在visited中的對應位置改成true,直到隊列不爲空,遍歷結束 } } return new GraphIterator(rls.iterator());
深度優先遍歷與廣度優先遍歷的代碼思想基本是相同的,但前者運用的是棧,進出的原則不一樣因此輸出的順序有差異。同時,另外一點區別是,在頂點還沒有添加到無序列表以前,不會將頂點設置爲已訪問網絡
問題1:關於最小生成樹「mstNetwork」這一優雅算法的不優雅理解數據結構
public Graph nstNetwork() { int x, y; int index; int weight; int[] edge = new int[2]; HeapADT<Double> minHeap = new LinkedHeap<Double>(); Network<T> resultGraph = new Network<T>(); if (isEmpty() || !isConnected()) return resultGraph;//當圖是空的或者沒有連通的時候返回一個空的引用 resultGraph.adjMatrix = new boolean[numVertices][numVertices];//建立一個鄰接矩陣 for (int i = 0; i < numVertices; i++) for (int j = 0; j < numVertices; j++) resultGraph.adjMatrix[i][j] = Double.POSITIVE_INFINITY; resultGraph.vertices = (T[])(new Object[numVertices]); boolean[] visited = new boolean[numVertices]; for (int i = 0; i < numVertices; i++) visited[i] = false; //把全部的頂點都設置爲未訪問過的 edge[0] = 0; resultGraph.vertices[0] = this.vertices[0]; resultGraph.numVertices++; visited[0] = true; // Add all edges that are adjacent to vertex 0 to the stack. for (int i = 0; i < numVertices; i++) { minHeap.addElement(new Double(adjMatrix[0][i])) }//將全部含起始頂點的邊按照權重次序添加到最小堆中 while ((resultGraph.size() < this.size()) && !minHeap.isEmpty()) { // Pop an edge off the stack and add it to the resultGraph. do{ weight = (minHeap.removeMin()).doubleValue(); edge = getEdgeWithWeightOf(weight, visited); }//從堆中取出最小邊 while(!indexIsValid(edge[0]) || !indexIsValid(edge[1])); x = edge[0]; y = edge[1]; if (!visited[x]) index = x; if (!visited[y]) index = y; //從最小邊中的兩個頂點中選出未訪問的一個頂點,並將索引值賦給index resultGraph.vertices[index] = this.vertices[index]; visited[index] = true; resultGraph.numVertices++;//將新頂點的信息賦給相應數組 resultGraph.adjMatrix[x][y] = this.adjMatrix[x][y]; resultGraph.adjMatrix[y][x] = this.adjMatrix[y][x]; // 添加全部含該新頂點且另外一頂點尚不在最小生成樹中的邊,直到最小樹中包含圖中的全部頂點或者最小堆中已經沒有元素 for (int i = 0; i < numVertices; i++) { if (!visited[i] && this.adjMatrix[i][index] < Double.POSITIVE_INFINITY) { edge[0] = index; edge[1] = i; minHeap.addElement(new Double(adjMatrix[index][i])); } } } return resultGraph; }
目前還不太可以理解edge[]的使用以及其中的部分代碼所起的做用
按照教材給出的提示,edge[]彷佛是,表示每條邊的一個三元數組,其中包含起始頂點、終止頂點和權重,可是這裏的定義又是int[] edge = new int[2]
,裏面只包含了兩個存儲空間,照理說就不是用做存儲邊的數組了。包括其餘一些代碼的問題,好比一開始設置的resultGraph.adjMatrix[i][j] = Double.POSITIVE_INFINITY;
是什麼意思。算法的核心思想大概是理解了,代碼實現好像還差的很遠。
--- 分割線 ---
以上問題的回答有參照同窗的博客以及與同窗的討論。
首先Double.POSITIVE_INFINITY表示的是無限大,resultGraph.adjMatrix[i][j] = Double.POSITIVE_INFINITY;
在這裏起到的做用,就和一個Boolean型的鄰接矩陣,將它的初始值所有設置爲false是一個道理。因此整個代碼的前面一部分,所有是在定義實現最小生成樹所須要的變量。除了剛纔提到的鄰接矩陣以外,還包含了一個存儲訪問信息的Boolean數組--visited,存儲起始頂點和終止頂點索引值信息的數組edge,存儲每個頂點信息的數組resultGraph.vertices = (T[])(new Object[numVertices]);
。
該最小生成樹的生成的起始頂點,默認爲了鄰接矩陣的索引值爲0的元素,因此首先設置
edge[0] = 0; resultGraph.vertices[0] = this.vertices[0]; resultGraph.numVertices++; visited[0] = true;
將全部數組都從起始頂點開始,利用for循環將含起始頂點的邊的權重值依次添加進最小堆中,再依據最小堆的性質將權重最小的數彈出,據此找到相應的最小邊,再根據最小邊找到兩個頂點中未訪問的那一個頂點,並以此爲新的起始頂點,去找下一個最小邊,直到最小樹中含有圖中的全部頂點爲止,也就是resultGraph.size() < this.size()) && !minHeap.isEmpty()
時
以上的分析都是基於我本身的理解,有一些部分我也沒琢磨明白,還有不完備的地方,但願多多指正
基於評分標準,我給譚鑫的博客打分:5分。得分狀況以下:
正確使用Markdown語法(加1分)
模板中的要素齊全(加1分)
教材學習中的問題和解決過程, 一個問題加1分
代碼調試中的問題和解決過程, 兩個問題加2分
基於評分標準,我給方藝雯的博客打分:8分。得分狀況以下
正確使用Markdown語法(加1分):
模板中的要素齊全(加1分)
教材學習中的問題和解決過程, 兩個問題加2分
代碼調試中的問題和解決過程, 四個問題加4分
書上的代碼愈來愈很差理解了,而後本週開始要進行結對的小組編程,抱緊大腿,盡力跟上你們的步伐
代碼行數(新增/累積) | 博客量(新增/累積) | 學習時間(新增/累積) | 重要成長 | |
---|---|---|---|---|
目標 | 5000行 | 30篇 | 400小時 | |
第一週 | 0/0 | 1/1 | 8/8 | |
第二週 | 470/470 | 1/2 | 12/20 | |
第三週 | 685/1155 | 2/4 | 10/30 | |
第四周 | 2499/3654 | 2/6 | 12/42 | |
第六週 | 1218/4872 | 2/8 | 10/52 | |
第七週 | 590/5462 | 1/9 | 12/64 | |
第八週 | 993/6455 | 1/10 | 12/76 | |
第九周 | 1192/7467 | 2/12 | 10/86 | |
第十週 | 1375/8842 | 1/13 | 12/98 |