20172323 2018-2019-1 《程序設計與數據結構》第九周學習總結

20172323 2018-2019-1 《程序設計與數據結構》第九周學習總結

教材學習內容總結

本週學習了第15章
圖和圖論銜接了數學和計算機科學的整個分支學科php


  • 15.1 無向圖
  • 圖由結點和結點間鏈接而成,這些結點叫作頂點,結點之間的鏈接叫作邊。邊(A,B)就意味着從頂點A到頂點B有一條邊
  • 無向圖是一種邊爲無序結點對的圖,也就意味着邊(A,B)是A和B之間有一條從兩個方向均可以遊歷的鏈接。也就是說(B,A)和(A,B)的含義是同樣的

  • 若是圖中的兩個頂點之間有一條連通邊,則稱這兩個頂點是鄰接的。如上圖,A和B是鄰接的,A和C不是鄰接的。鄰接頂點有時稱爲鄰居,連通一個頂點及其自身的邊稱爲自循環或環。
  • 若是無向圖擁有最大數目的連通頂點的邊,則認爲這個無向圖是徹底的html

    對有n個頂點的無向圖,要使該圖是徹底的,要求有n(n-1)/2條邊node

  • 路徑是圖中的一系列邊,每條邊連通兩個頂點。路徑的長度是該路中邊的條數或者是頂點數減去1.
  • 樹也是圖的一種
  • 若是無向圖中的任意兩個頂點之間都存在一條路徑,則認爲這個無向圖是連通的。
  • 環路是一種首頂點和末頂點相同且沒有重邊的路徑。無向樹是一種連通的無環無向圖,其中一個元素被指定爲樹根。git


  • 15.2 有向圖
  • 有向圖也被稱爲雙向圖,它是一種邊爲有序頂點對的圖
  • 有向圖中的路徑是圖中連通兩個頂點的有向序列。
  • 若是有向圖中沒有環路,且有一條從A到B的邊,則能夠把頂點A安排在頂點B以前,這樣排列獲得的頂點次序稱爲拓撲序。web

    • 有向樹是一種指定了一個元素作爲樹根的有向圖,該圖含有如下性質
      - 不存在其餘頂點到樹根的鏈接
      - 每一個非樹根元素剛好有一個鏈接
      - 樹根到每一個其餘頂點都有一條路徑

  • 15.3 網絡
  • 網絡,或稱爲加權圖,是一種每條邊都帶有權重或代價的圖。加權圖中的路徑權重是該路徑中各條邊權重的和
  • 根據須要,網絡能夠是無向的,也能夠是有向的。對於網絡,咱們將用一個三元組來表示每條邊,其中包括其實頂點、終止頂點和權重

  • 15.4 經常使用的圖算法
  • 遍歷:圖的遍歷分爲兩類,廣度優先遍歷和深度優先遍歷。圖中不存在根結點,所以圖的遍歷能夠從其中的任一頂點開始。
  • 圖的深度優先遍歷與廣度優先遍歷的惟一不一樣是,它使用的是棧而不是隊列來管理遍歷
  • 測試連通性:不論哪一個爲起始頂點,當且僅當廣度優先遍歷中的頂點數目等於圖中的頂點數目時,該圖纔是連通的。
  • 最小生成樹:生成樹是一棵含有圖中全部頂點和部分邊的樹。最小生成樹是這樣一棵生成樹,其邊的權重總和小於或等於同一個圖中其餘任何一棵生成樹的權重總和。
  • 斷定最短路徑:一種是斷定起始頂點與目標頂點之間的最小邊數。一種是尋找加權圖的最便宜路徑

  • 15.5 圖的實現策略
  • 鄰接列表:鄰接列表運用到的是二維數組,這個二維數組中的每一個單元都表示了兩個頂點的交接狀況,由布爾值來表示。無向圖的鄰接矩陣是對稱的。
  • 15.6 用鄰接矩陣實現無向圖

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

  • 問題1:無向圖的廣度優先遍歷中,第一個頂點有三個鄰接頂點,那麼下一次遍歷的時候必須從最右一個頂點開始遍歷嗎?
  • 問題1解決方案:相似於樹的遍歷,在同一層中,元素的遍歷都是從左到右的進行遍歷,圖中看似是從右邊一個元素開始的,可是第一個遍歷的頂點按照樹的結構畫出來,依舊是從左至右開始遍歷。如圖是一個圖的遍歷的順序

    能夠看到,每一層都是從左至右地遍歷的。若是是已經查看過(visited)的,那麼就能夠跳過查看它的下一個

  • 問題2:addEdge方法裏運用到的幾個方法getIndex、indexIsValid分別是什麼意思
  • 問題2解決方案:由於無向圖的實現是由一個頂點列表和鄰接矩陣組合而成的,因此若是要在兩個頂點之間添加一條邊,首先須要在頂點列表中找到這兩個頂點,getIndex就是這樣一個方法,用於定位正確的索引。indexIsValid則是用於判斷索引值是否合法,若是合法的話就把鄰接矩陣內兩個頂點之間對應的值改成true。另外一方面,頂點列表的索引值能夠用於鄰接矩陣,譬如頂點列表索引值爲一的位置的元素也就是鄰接矩陣第一行第一個或者第一列第一個表示的值。因此代碼實現時在這裏能夠直接寫adjMatrix[index1][index2] = true, adjMartix[index2][index1] = true

  • 問題3:深度優先遍歷的遍歷順序以及兩種遍歷的代碼理解
  • 問題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」這一優雅算法的不優雅理解數據結構

  • 問題1解決方案:最小生成樹的實際意義就在於從複雜的網絡中找到一條最便捷的路將全部頂點串起來,便捷的具體含義就是每條邊的權重之和小於或等於同一圖中任何其餘生成樹的權重之和,等於說明一個圖中可能不止一個最小生成樹。
    代碼理解
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()
以上的分析都是基於我本身的理解,有一些部分我也沒琢磨明白,還有不完備的地方,但願多多指正

代碼託管

上週考試錯題總結


  • Since a heap is a binary search tree, there is only one correct location for the insertion of a new node, and that is either the next open position from the left at level h or the first position on the left at level h+1 if level h is full.
    A .True
    B .Flase
  • 堆是一棵徹底樹,不是單純的只是一棵二叉查找樹

結對及互評

  • 博客中值得學習的或問題:
    • 教材學習內容都總結的很好,關於最小生成樹的兩個算法都作了很詳細的解釋,圖片雖然作的醜可是很清晰,建議助教加分
  • 基於評分標準,我給譚鑫的博客打分: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

參考資料

相關文章
相關標籤/搜索