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

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

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

教材學習內容總結

1.1、堆

  • 堆(heap):是具有兩個附加屬性的二叉樹。一是堆是一顆完全樹(如果一棵二叉樹是平衡的,即所有葉子都位於h或h-1層,其中h為log2n, 且n是樹中的元素數目,且所有h層中的葉子都位於該樹的左邊,那麼該樹就被認為是完全的。),二是對每一結點,它小於或等於(大於或等於)其左孩子和右孩子。
  • 小於或等於其左孩子和右孩子的是最小堆(minheap),結點大於或等於它的左右孩子是最大堆(maxheap), 其中的結點大於或等於它的左右孩子。關鍵概念

  • 最小堆將其最小元素儲存在該二叉樹的根處,且其根的兩個孩子同樣也是最小堆。

  • 堆的介面:


1.2、addElement操作

  • addElement方法將給定的Comparable元素新增到堆中的恰當位置處,且維持該堆的完全性屬性和有序屬性。所以完成addElement方法有兩個步驟,一是將元素放入堆中,二是調整元素的位置,保持堆的性質。
    • 放入的位置:一個堆就是棵完全樹, 所以對於插入的新結點而言只存在一個正確的位置, 且它要麼是h層左邊的下一個空位置,要麼是h+1層左邊的第1個位置(如果h層是滿的話)
    • 排序屬性:第一將該新元素和其父親的值進行比較,如果該新結點小於其雙親則將它們互換;然後重複,沿著樹向上,直至該新元素要麼是大於其雙親要麼是位於該堆的根處。在堆實現中,我們會對樹中的最末個結點,進行跟蹤記錄。

1.3、 removeMin 操作

  • 最小元素是儲存在最小堆的根處的,所以要做的就是返回根元素並用堆中的另一元素替換它。
    • 替換:與addElement操作一樣,是要維持該樹的完全性,那麼只有一個能替換根的合法元素,那就是是儲存在樹中最末片葉子上的元素。
    • 重排:將該新根元素與其較小的孩子進行比較,且如果孩子更小則將它們互換。沿著樹向下繼續這過程,直到該元素要麼位於某葉子中, 要麼比它的兩個孩子都小。

1.4、findMin操作

  • 堆中最小元素元素總是被儲存在該樹的根處,所以實現findMin方法只需通過返回儲存在根處的元素。

2.1、使用堆: 優先順序佇列

  • 優先順序佇列(priority queue) 是遵循兩個排序規則的集合。
    • 具有更高優先順序的專案在先。
    • 具有相同優先順序的專案使用先進先出方法來確定其排序。
  • 優先順序佇列實現方式有兩種使用佇列列表或最小堆。雖然最小堆根本就不是一個佇列,但是它卻提供了一個高效的優先順序佇列實現。
  • 過程:建立個PriorityQueueNode物件,用來儲存將被放置在佇列中的元素、該元素的優先順序、以及元素放進佇列的順序。然後,使用compareTo 方法,對優先順序進行比較,然後在優先順序相同的時候再對階進行比較。

3、用連結串列實現堆

  • 因為我們要求在插入元素後能夠向上遍歷該樹,所以堆中結點必須儲存指向其雙親的指標。
  • 堆介面中定義的方法用連結串列實現,實現的演算法和邏輯就是上述所寫的。
    • 連結串列實現中例項化一個HeapNode類的LastNode引用,來跟蹤記錄堆中的最末一片葉子。
    • addElement方法還呼叫了兩個私有方法來實現上述的兩個步驟,其操作的複雜度為2×log n +1+log n ,即O(log n)。
    • addElement方法中呼叫的第二個重排序的方法實際上並沒有執行雙親與孩子的完整互換,它只是把雙親元素往下平移到正確的插入點,其複雜度也是O(log n)。但是,它的確提高了效率,因為它減少了在堆的每一層上要執行的賦值次數。
    • removeMin方法同理,時間複雜度為2Xlogn+logn+1,即O(log n)。
    • findMin方法的時間複雜度為O(1)。

4、用陣列實現堆

  • 在二叉樹的陣列實現中,樹的根位於位置0處,對於每一結點n, n的左孩子將位於陣列的2n+1位置處,n的右孩子將位於陣列的2(n+ 1)位置處,因此我們能夠計算
    雙親和孩子的位置。
  • 陣列實現的addElement方法必須首先檢查可用空間,如需要,要對該陣列進行擴容。
    • 與連結串列實現不同,陣列實現不需要確定新結點雙親的步驟。
    • 陣列實現的addElement操作的時間複雜度與連結串列實現的複雜度一樣為1+log n或O(log n)。
  • 連結串列實現和陣列實現的removeMin操作的複雜度同為O(logn),
  • findMin 操作與連結串列實現的一樣, 其複雜度為O(1)。

5、使用堆:堆排序

  • 使用一個堆來對某個數字列表進行排序,只需將列表的每元素新增到堆中, 然後一次一個地將它們從根中刪除。 在最小堆的情形下,排序結果將是該列表以升序排列。在最大堆的情形下,排序結果將是該列表以降序排列。
  • 對於堆的排序演算法,我們需要執行addElemcmt和removeElement兩個操作n次,即列表中每個元素一次。 因此,最終的複雜度為2XnXlogn,即O(nlog n)。

藍墨雲上做的一個練習就很清楚地顯示了這個過程。

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

  • 問題1:課本多次說到完成一些操作例如:addElement和removeMin等操作,陣列和連結串列實現的堆的複雜度是相同的,但是還是說陣列實現的效率更高,這是為什麼?陣列和連結串列實現堆的優缺點分別是什麼?

  • 問題1解決方案:
    用陣列實現堆時,進行addElement操作不需要和連結串列一樣重新確定新節點雙親,removemin操作不需要確定新的最末結點。

簡單總結一下,用陣列實現堆的好處有

優點:和順序儲存一樣的
1、可以很方便找到孩子、雙親和兄弟,以及祖先和子孫
2、只需要儲存資料元素,不需要另外儲存元素的邏輯關係
3、物理結構和邏輯結構一致

  • 問題2:程式碼解讀。下面這一段程式碼是用連結串列實現堆的addElement操作時的返回插入結點雙親的那個節點的引用,但是我分情況討論的過程中一直出現理解不了的地方。
 private HeapNode<T> getNextParentAdd()
    {
        HeapNode<T> result = lastNode;

        while ((result != root) && (result.getParent().getLeft() != result))
            result = result.getParent();

        if (result != root)
            if (result.getParent().getRight() == null)
                result = result.getParent();
            else
            {
                result = (HeapNode<T>)result.getParent().getRight();
                while (result.getLeft() != null)
                    result = (HeapNode<T>)result.getLeft();
            }
        else
            while (result.getLeft() != null)
                result = (HeapNode<T>)result.getLeft();
          
        return result;
    }
  • 問題2解決方案:第一遍看的時候,把自己的書畫的亂七八糟的都沒有理解這些程式碼的意思,

然後就去問馨雨同學,這些程式碼的意思,結果討論討論著,馨雨馨雨就被我問蒙了,O(∩_∩)O,後來,發現是我們理解錯了lastNode結點,“插入結點的雙親結點”,之前一直理解的是將要插入的結點的雙親結點,其實lastNode是最後一片葉子結點,情況如下:

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

  • 問題1:

ArrayHeap類的程式碼書上是有給出的,然後自己寫了一個測試類,結果發現出現了這樣的問題

  • 問題1解決方案:想要進行除錯看一下,結果發現

    (這個問題我不知道是為什麼發生的o(╥﹏╥)o)

然後又仔細思考了一下程式碼,在程式碼中將最後一個元素賦值給了root結點,但是原因是並沒有將葉結點刪除,所以接下來重新調平衡的時候,最後的那個位置並進行操作,所以出現了重複。

程式碼託管

(statistics.sh指令碼的執行結果截圖)

上週考試錯題總結

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

    What type does "compareTo" return?
    A
    .
    int
    B
    .
    String
    C
    .
    boolean
    D
    .
    char

解析:compareTo()方法確實是String類中的一個,但是compareTo()的返回值是int。

它先比較對應字元的ASCII碼,如字串的某字元與引數的某字元不等,則結束比較,返回它們ASCII碼的差值。直至字串的字元或引數的字元 有一方全比較完,此時比較字串的長度差並返回。

結對及互評

點評:

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

    • 挺逗的,可以用來吸引目光。部落格的頁面又進行了修改,挺好看的。
  • 程式碼中值得學習的或問題:
    • 設計的思路很清晰,不像我一樣總是很奇葩,^_^。

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

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

這周又是Java課多到爆表的雙週,我們又開始完成實驗二了,而且這周老師還讓我們組了個隊,要完成一個Java專案的開發,ε=ε=ε=(#>д<)ノ,難。
這個學期越來越注重程式碼的編寫能力,也接觸到了很多的新的演算法和知識點,就像這個禮拜學的堆,希望自己接下來的課程要繼續努力。

學習進度條

程式碼行數(新增/累積) 部落格量(新增/累積) 學習時間(新增/累積)
目標 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
  • 計劃學習時間:30小時

  • 實際學習時間:32小時

  • 改進情況:這周是雙週,Java課比較多,課程內容也比較多,保持了良好的學習進度和時間。

參考資料