20172305 2018-2019-1 《Java軟件結構與數據結構》第九周學習總結

20172305 2018-2019-1 《Java軟件結構與數據結構》第九周學習總結

教材學習內容總結

本週內容主要爲書第十五章內容:

  • (結點和結點之間的鏈接構成)
    • 頂點:結點
    • 邊:結點之間的鏈接
    • 鄰接:兩個結點之間有一條連通邊,則兩個結點是鄰接的,有時鄰接頂點稱爲鄰居。
    • 自循環(環):連通一個頂點及其自身的邊
    • 環路:一種首頂點和末頂點相同且沒有重邊的路徑,沒有環路的圖稱爲無環的。
  • 無向圖:一種邊爲無序結點對的圖html

    • 若是無向圖擁有最大數目的連通頂點的邊,則認爲這個無向圖是徹底的java

      • 對有n個頂點的無向圖,要使圖徹底就要求有n(n-1)/2條邊。
    • 若是無向圖中的任意兩個頂點之間都存在一條路徑,則認爲這個無向圖是連通的git

    • 無向圖的路徑是雙向的。
    • 路徑:圖中一系列邊,每條邊連通兩個頂點。
    • 無向樹是一種連通的無環無向圖,其中一個元素被指定爲樹根。算法

  • 有向圖(雙向圖):邊爲有序頂點對的圖,eg:邊(A,B)容許從A向B遊歷,但不容許反方向的遊歷。數組

    • 若是有向圖的每兩個頂點之間都有兩條方向相反的邊鏈接,則認爲這個有向圖是徹底的網絡

    • 若是有向圖的任意兩個頂點之間都存在一條路徑,且鏈接兩個頂點的路徑中全部的邊都必須同向,則認爲這個有向圖是連通的數據結構

    • 拓撲序(排列獲得的頂點次序):若是有向圖中沒有環路,且有一條從A到B的邊,則能夠把頂點A安排在頂點B以前。
    • 有向圖的路徑是單向的。
    • 路徑:圖中連通兩個頂點的有向邊序列。
    • 有向樹是一種指定了一個元素做爲樹根的有向圖。學習

      • 不存在其餘頂點到樹根的鏈接
      • 每一個非樹根元素剛好有一個鏈接
      • 樹根到每一個其餘頂點都有一條路徑
  • 網絡(加權圖):一種每條邊都帶有權重或代價的圖
    • 三元組來表示每條邊:起始頂點、終止頂點、權重測試

      • 無向網絡的起始頂點與終止頂點能夠互換
      • 有向圖必須包含每一個有向鏈接的三元組
  • 圖算法--遍歷
    • 廣度優先遍歷(相似樹的層序遍歷)優化

      • 首先從一個未走到過的頂點做爲起始頂點,好比元素0頂點做爲起點。
      • 沿0頂點的邊去嘗試訪問其它未走到過的頂點,首先發現2頂點尚未走到過,因而來到了2頂點。
      • 返回到0頂點,再以0頂點做爲出發點繼續嘗試訪問其它未走到過的頂點,這樣來到了1頂點。
      • 返回到0頂點,再以0頂點做爲出發點繼續嘗試訪問其它未走到過的頂點,這樣來到了5頂點。
      • 可是,此時沿0頂點的邊,已經不能訪問到其它未走到過的頂點了,因此須要返回到2頂點。
      • 沿2頂點的邊去嘗試訪問其它未走到過的頂點,(由於1頂點已經走過了)首先發現3頂點尚未走到過,因而來到了3頂點。
      • 返回到2頂點,再以2頂點做爲出發點繼續嘗試訪問其它未走到過的頂點,這樣來到了4頂點。
      • 可是,此時沿4頂點的邊,已經不能訪問到其它未走到過的頂點了,至此,全部頂點咱們都走到過了,遍歷結束。
    • 深度優先遍歷(相似樹的前序遍歷)

      • 首先從一個未走到過的頂點做爲起始頂點,好比元素0頂點做爲起點。
      • 沿0頂點的邊去嘗試訪問其它未走到過的頂點,首先發現2頂點尚未走到過,因而來到了2頂點。
      • 再以2頂點做爲出發點繼續嘗試訪問其它未走到過的頂點,這樣又來到了1頂點。
      • 再以1號頂點做爲出發點繼續嘗試訪問其它未走到過的頂點。
      • 可是,此時沿1頂點的邊,已經不能訪問到其它未走到過的頂點了,因此須要返回到2頂點。
      • 返回到2號頂點後,以2頂點做爲出發點繼續嘗試訪問其它未走到過的頂點,此時又會來到3頂點,再以3號頂點做爲出發點繼續訪問其它未走到過的頂點,因而又來到了5號頂點。
      • 可是,此時沿5頂點的邊,已經不能訪問到其它未走到過的頂點了,因此須要返回到3頂點。
      • 返回到3頂點後,以3頂點做爲出發點繼續嘗試訪問其它未走到過的頂點,此時會來到4頂點,再以4頂點做爲出發點繼續訪問其它未走到過的頂點。
      • 可是,此時沿4頂點的邊,已經不能訪問到其它未走到過的頂點了,至此,全部頂點咱們都走到過了,遍歷結束。
    • 圖的遍歷能夠從任一頂點開始
    • 深度優先遍歷和廣度優先遍歷的位移不一樣是使用到了棧而不是隊列來管理遍歷。

  • 圖算法--測試連通性
    • 在一個圖中,不管哪一個爲起始頂點,當且僅當廣度優先遍歷中的頂點數等於圖中的頂點數目時,則該圖是連通的。
  • 圖算法--最小生成樹
    • 生成樹是一棵含有圖中全部頂點和部分邊(可能不是全部邊)的樹。
    • 最小生成樹是邊的權重總和和小於或等於同一個圖中其餘任何一棵生成樹的權重總和。

      • 非連通的無向圖,不存在最小生成樹
      • 權重不必定和距離成正比
      • 權重多是0或負數
      • 若存在相等的權重,那麼最小生成樹可能不惟一
    • Prim算法:
  • 圖算法--判斷最短路徑
    • 方法一:斷定起始頂點與目標頂點之間的字面意義上的最短路徑,即兩個頂點之間的最小邊數。
    • 方法二:Dijkstra算法

    • 以0頂點爲開始位置,0頂點到1頂點的權重爲3,到2頂點的權重爲max,到3頂點的權重爲4,到4頂點的權重爲5。

    • 選取權重最小的頂點爲1頂點,由於全部的權重都爲正,那麼0頂點到1頂點的最短距離就是3,再以1頂點開始,1頂點能夠到4頂點和2頂點,到4頂點的位置是3+1=4<5,即0頂點到4頂點的最短距離是經過1頂點的,再看到2頂點的位置是3+10=13<max,即0頂點直接到2頂點的距離爲max,可是經過1頂點能夠縮短爲13。

    • 扣除0頂點和1頂點已經肯定好了最短距離以後,選取最短的權重爲0頂點到3頂點爲4。

    • 以3頂點開始,3頂點能夠到2頂點,到2頂點的位置是4+7=11<13,即0頂點到2頂點的最短距離,從max到經過1頂點再到經過3頂點到2頂點。

    • 扣除0頂點、1頂點和3頂點,已經肯定好了最短距離以後,選取最小的權重爲0頂點到1頂點再到4頂點爲4,4頂點能夠到1頂點、3頂點和2頂點,可是3頂點和1頂點已經肯定好了,只剩2頂點,那麼到2頂點的位置爲3+1+6=10<11,即到2頂點的最短距離改成經過1頂點、4頂點最後到2頂點。

    • 最後扣除0頂點、1頂點、3頂點和4頂點,只剩下2頂點,距離只剩10,因此到2頂點的最短距離爲10,結束算法。

  • 圖的實現策略
    • 鄰接列表

      • 在鄰接表的表示中,無向圖的同一條邊在鄰接表中存儲的兩次。若是想要知道頂點的讀,只須要求出所對應鏈表的結點個數便可。
      • 有向圖中每條邊在鄰接表中只出現一此,求頂點的出度只須要遍歷所對應鏈表便可。求出度則須要遍歷其餘頂點的鏈表。
        無向圖的鄰接列表:

        有向圖的鄰接列表:
    • 鄰接矩陣

      • 在鄰接矩陣表示中,無向圖的鄰接矩陣是對稱的。矩陣中第 i 行或 第 i 列有效元素個數之和就是頂點的度。
      • 在有向圖中 第 i 行有效元素個數之和是頂點的出度,第 i 列有效元素個數之和是頂點的入度。
        無向圖的鄰接矩陣:

        有向圖的鄰接矩陣:
    • 鄰接矩陣與鄰接表優缺點:鄰接矩陣的優勢是能夠快速判斷兩個頂點之間是否存在邊,能夠快速添加邊或者刪除邊。而其缺點是若是頂點之間的邊比較少,會比較浪費空間。由於是一個 n∗nn∗n 的矩陣。而鄰接表的優勢是節省空間,只存儲實際存在的邊。其缺點是關注頂點的度時,就可能須要遍歷一個鏈表。還有一個缺點是,對於無向圖,若是須要刪除一條邊,就須要在兩個鏈表上查找並刪除。
    • 邊集數組

      • 邊集數組由兩個一維數組構成:一個存儲頂點信息; 一個存儲邊的信息,這個邊數組每一個數據元素由一條邊的起點下標(begin)、終點下標(end)、和權(weight)組成。
      • 邊集數組關注的是邊的集合,在邊集數組中要查找一個頂點的度須要掃描整個邊數組,效率並不高。所以它更適合對邊依次進行處理的操做,而不適合對頂點相關的操做。
        無向圖的邊集數組:

        有向圖的邊集數組:
    • 十字鏈表

      • 十字鏈表是爲了便於求得圖中頂點的度(出度和入度)而提出來的。用十字鏈表來存儲有向圖,能夠達到高效的存取效果。它是綜合鄰接表和逆鄰接表形式的一種鏈式存儲結構。

        綠色鏈表表示以結點A爲弧頭的弧組成的鏈表。黃色鏈表表示以結點A爲弧尾的弧組成的鏈表。
    • 鄰接多重表

      • 鄰接多重表主要用於存儲無向圖。若是用鄰接表存儲無向圖,每條邊的兩個邊結點分別在以該邊所依附的兩個頂點爲頭結點的鏈表中,這給圖的某些操做帶來不便。所以,在進行這一類操做的無向圖的問題中採用鄰接多重表做存儲結構更爲適宜。

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

  • 問題1:如何測試有向圖和無向圖的連通性?
  • 問題1解決方案:無向圖的連通性在書上已經給出,字面理解就是任意兩點之間都存在一條路徑,而從編寫的角度就是一個頂點開始深度優先遍歷或是廣度優先遍歷得出的頂點數。而有向圖的是具備方向的,若是這樣的話咱們如何斷定有向圖的連通性?在查詢的過程當中,發現有向圖的連通性能夠分紅強連通圖、弱連通圖以及單向連通圖。書上的圖15.4的連通圖就是一個弱連通圖,若是是強連通圖則須要在此基礎上每兩點之間都添加一條與之方向相反的線。針對有向圖的測試連通性的方法能夠用到Tarjan算法

    • 在有向圖中, 若對於任意兩個兩點之間, 都存在互逆的路徑,則稱此圖是強連通圖。即有向圖中,若對於任意兩個不一樣的頂點x和y,都存在從x到y以及從y到x的路徑,則稱G是強連通圖。
    • 有向圖的全部的有向邊替換爲無向邊,所獲得的圖稱爲原圖的基圖。若是一個有向圖的基圖是連通圖,則有向圖是弱連通圖。
    • 若是有向圖中,對於任意節點v1和v2,至少存在從v1到v2和從v2到v1的路徑中的一條,則原圖爲單向連通圖。
    • 強連通圖、連通圖、單向連通圖三者之間的關係是,強連通圖必然是單向連通的,單向連通圖必然是弱連通圖。
    • 在Tarjan算法中爲每一個節點i維護瞭如下幾個變量:
      DFN[i]:深度優先搜索遍歷時節點i被搜索的次序。
      low[i]:節點i可以回溯到的最先位於棧中的節點。
      flag[i]:標記幾點i是否在棧中。
    • Tarjan算法的運行過程:
      (1)首先就是按照深度優先搜索算法搜索的次序對圖中全部的節點進行搜索。
      (2)在搜索過程當中,對於任意節點u和與其相連的節點v,根據節點v是否在棧中來進行不一樣的操做:
      a.節點v不在棧中,即節點v尚未被訪問過,則繼續對v進行深度搜索。
      b.節點v已經在棧中,即已經被訪問過,則判斷節點v的DFN值和節點u的low值的大小來更新節點u的low值。若是節點v的 DFN值要小於節點u的low值,根據low值的定義(可以回溯到的最先的已經在棧中的節點),咱們須要用DFN值來更新u 的low值。
      (3)在回溯過程當中,對於任意節點u用其子節點v(其實不能算是子節點,只是在深度遍歷的過程當中,v是在u以後緊挨着u的節點)的   low值來更新節點u的low值。由於節點v可以回溯到的已經在棧中的節點,節點u也必定可以回溯到。由於存在從u到v的直接路徑,因此v可以到的節點u也必定可以到。
      (4)對於一個連通圖,咱們很容易想到,在該連通圖中有且僅有一個節點u的DFN值和low值相等。該節點必定是在深度遍歷的過程當中,該連通圖中第一個被訪問過的節點,由於它的DFN值和low值最小,不會被該連通圖中的其餘節點所影響。

代碼學習中的問題和解決過程

  • 問題1:如何理解書中深度和廣度的遍歷代碼?遞歸形式如何表示?
  • 問題1的解決方案:廣度優先遍歷和深度優先遍歷的區別是在於廣度優先遍歷用的是隊列,深度優先遍歷用的是棧,一個是先進先出另外一個後進先出。
    • 遍歷是建立了一個無序列表和棧或是隊列,隊列或棧用來存放的是索引值,無序列表存儲的是結果,先進行判斷若是你想查找的的索引值是超出用來存放內容的數組的長度的話,就會直接迭代無序列表,注意此時的無序列表是沒有內容的,因此迭代出的內容也什麼也沒有。若是沒有超過則建立一個布爾的一維數組用來記錄每一個索引值對應的內容是否被查過,先記錄每一個索引值都未被查到,再將開始查找元素的索引值存入棧或隊列中,並將該索引值對應的布爾型數組內存放的false改成true來肯定是該元素已經查過。進入while循環變量x是剛入棧或是隊列的索引值,廣度優先遍歷是將該索引值對應的內容添加到無序列表的尾部,再進入for循環不斷將爲未存元素存入隊列中,經過判斷存入隊列的元素是否與x元素有邊,若是有邊還未被查找的元素就會被存入隊列中,而後跳出for循環在while循環中此時更換出隊的索引值,再進入for循環經過判斷條件來肯定繼續存放在隊列中的索引值,這樣經過不一樣的索引值來實現圖上的每個元素都會連着下一個元素,相似層序遍歷的思路。而深度優先遍歷循環體條件和判斷條件都相同,可是判斷條件內部不一樣,須要輔助一個布爾型的變量來判斷是否與查找元素有聯繫,深度優先遍歷與廣度優先遍歷不一樣的是深度有可能會遇到該元素沒有與之相連的邊的元素未被查到,而與它相連的邊的被查元素還有邊和未查元素,這樣就須要返回到上一個,在從上一個開始進行循環,而多出的那個判斷條件就是肯定該元素是否符合上述可能狀況若是符合的話就會彈出,到上一個元素位置進行查找,這是廣度優先遍歷不會遇到的狀況,也是改用棧的緣由,深度優先遍歷相似與前序遍歷的思路。根據書上所寫能夠用遞歸的方法編寫代碼,附遞歸版代碼(廣度優先遍歷的代碼在網上查找的)
    public void DFSRecursion() {
          boolean[] visited = new boolean[numVertices];
          for (int i = 0; i < numVertices; i++)
              visited[i] = false;
          for(int i=0;i<numVertices;i++) {
              if (!visited[i]) {
                  depthFirstSearch(visited,i);
              }
          }
      }
      private void depthFirstSearch(boolean[] isVisited,int  i) {
          System.out.print(vertices[i]+" ");
          isVisited[i]=true;
          int w=getFirstNeighbor(i);
          while (w!=-1) {
              if (!isVisited[w]) {
                  depthFirstSearch(isVisited,w);
              }
              w=getNextNeighbor(i, w);
          }
      }
    
      public void broadFirstSearch() {
          boolean[] visited = new boolean[numVertices];
          for (int i = 0; i < numVertices; i++)
              visited[i] = false;
          for(int i=0;i< numVertices;i++) {
              if(!visited[i]) {
                  broadFirstSearch(visited, i);
              }
          }
      }
      private void broadFirstSearch(boolean[] isVisited,int i) {
          int u,w;
          LinkedList queue=new LinkedList();
    
          System.out.print(vertices[i]+"  ");
          isVisited[i]=true;
          queue.addLast(vertices[i]);
          while (!queue.isEmpty()) {
              u=((Integer)queue.removeFirst()).intValue();
              w=getFirstNeighbor(u);
              while(w!=-1) {
                  if(!isVisited[w]) {
                      System.out.print(vertices[w]+"  ");
                      isVisited[w]=true;
                      queue.addLast(w);
                  }
                  w=getNextNeighbor(u, w);
              }
          }
      }

  • 問題2:PP15.7如何肯定最短路徑和最便宜路徑?
  • 問題2的解決方案:根據題目要求容許客戶輸入兩個城市以及兩個城市之間的價格,這就相似加權圖,根據GraphADT接口以及實現的添加頂點和添加邊的方法都沒有涉及到權重(這裏的權重爲兩個城市之間的價格),而無向圖實現的是肯定兩個頂點之間是否有邊,用一個二維布爾型數組存儲對應頂點是否有邊,咱們的想法是能夠將是否有邊來改成數字,用票價來實現是否有聯繫。那麼問題又來了,沒有聯繫的如何來界定?定義爲最大仍是最小?若是定義爲最小爲零的話,咱們的選擇就要在排除零的前提下進行Dijkstra算法,可是若是咱們將無聯繫的看做是無窮大的狀況下,就會產生不同的效果,在選取權重較小即最便宜路徑的時候就會很方便,並且事實上提供的算法也是要將無聯繫的設爲最大,就如同教材內容總結部分敘述的算法同樣。可是,不管是無窮大仍是無窮小可能是用什麼表示?和數序符號同樣麼?API提供瞭解決辦法(侯澤洋同窗提供):Double.POSITIVE_INFINITY表示的是正無窮大值,Double.NEGATIVE_INFINITY表示的是負無窮大值。這樣就能夠實現了,可是Dijkstra算法如何書寫呢?又是一個問題好喪...在網上查了幾個,沒有適用成功因此經過看侯澤洋的博客找到了一個不錯的博客,用他的算法來實現的具體代碼,並且侯澤洋的代碼層次設計的很是好,實現的也乾淨利落,很強大,是我值得學習的。此外,最短路徑就能夠用到Graph內的shortestPathLength方法和iteratorShortestPath實現,我在此基礎上經過返回最短路徑的數值,若是是1的話就會存在直達列車,大於1的話就會是不可直達,在同經過遍歷最短路徑的迭代方法進行輸出最短路徑就好。

代碼託管

上週考試錯題總結

錯題已經在上週博客寫過...

結對與互評

點評(王禹涵)

  • 博客中值得學習的或問題:
    • 廣度優先遍歷和深度優先遍歷的分析最好和最小生成樹的算法分析的那麼細緻就好,建議問題二的代碼能夠附上去。
  • 代碼中值得學習的或問題:
    • 分析了最小生成樹的Prim算法,很強大,經過你的分析有助於我對算法的理解,很棒!
  • 基於評分標準,我給本博客打分:7分。
    • 得分狀況以下:
    • 正確使用Markdown語法(加1分)
    • 模板中的要素齊全(加1分)
    • 教材學習中的問題和解決過程, 三個問題加3分
    • 代碼調試中的問題和解決過程, 一個問題加1分
    • 感想,體會不假大空的加1分
    • 點評認真,能指出博客和代碼中的問題的加1分

點評(方藝雯)

  • 博客中值得學習的或問題:
    • 圖片清晰,總結的也很到位,也能夠對本章的算法進行簡單的總結,博客中的問題二和代碼中的問題二能夠整合一下。
  • 代碼中值得學習的或問題:
    • 代碼中的問題總結的很好,特別是那個迭代器的廣度優先遍歷用到分析的特別細緻,建議能夠和算法內容進行聯繫,或是在分析代碼的過程當中查一些算法的相關知識。
  • 基於評分標準,我給本博客打分:9分。
  • 得分狀況以下:
    • 正確使用Markdown語法(加1分)
    • 教材學習中的問題和解決過程, 二個問題加2分
    • 代碼調試中的問題和解決過程, 四個問題加4分
    • 感想,體會不假大空的加1分
    • 點評認真,能指出博客和代碼中的問題的加1分

互評對象

感悟

第十五章的圖感受和紅黑樹同樣,要構造很亂的數據結構來實現,此外還有一堆算法能夠優化圖的操做(算法做用很好,可是難於理解,有的思路就難理解)。好在有些代碼給出了,可是書上的遍歷代碼就沒給全,致使始終讀不懂一個方法具體幹嗎的,具體實現的策略也不一樣於以前的數據結構。要保持好的心態來學習代碼,切莫急功近利...

學習進度條

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積) 重要成長
目標 5000行 30篇 400小時
第一週 0/0 1/1 15/15
第二週 703/703 1/2 20/35
第三週 762/1465 1/3 20/55
第四周 2073/3538 1/4 40/95
第五週 981/4519 2/6 40/135
第六週 1088/5607 2/8 50/185
第七週 1203/6810 1/9 50/235
第八週 2264/9074 2/11 50/285
第九周 2045/11119 1/12 50/335

參考資料

相關文章
相關標籤/搜索