20172301 《程序設計與數據結構》第八週學習總結

20172301 《程序設計與數據結構》第八週學習總結

教材學習內容總結

  • 堆:是具備兩個附加屬性的一棵二叉樹。
    • 是一棵徹底樹。
    • 最小堆對每一結點,它小於或等於其左孩子和右孩子。反之,最大堆對每個結點大於或等於它的左右孩子。
    • ,最小堆將其最小元素存儲在該二叉樹的根處,且最小堆根結點的子樹一樣也是最小堆。
  • addElement操做:將給定的元素添加到堆中的恰當位置,維持該堆的徹底性屬性和有序屬性
    • 若是元素不是Comparable類型的,則會拋出異常。這是爲了讓元素可比較,能夠維持堆的有序屬性。
    • 而爲了維護堆的徹底性,就決定了堆插入結點時只有兩種狀況:html

      1.h層的左邊下一個位置。
      2.h+1層的第一個位置。(h層爲滿)git

    • 將新元素添加到堆的末尾後,考慮到有序屬性,將該元素與父結點進行比較,若小將它與父結點對換,直到大於父結點或者位於根結點處。
  • removeMin操做:刪除堆的最小元素:刪除堆的最小元素而且返回。
    • 最小元素位於根結點,刪除掉根結點,爲了維持樹的徹底性,要找一個元素來替代它,那麼只有一個能替換根的合法元素,且它是存儲在樹中最末一片葉子上的元素。最末的葉子是h層上最右邊的葉子。
    • 考慮到有序屬性,對該堆從新排序。將新的根元素和其較小的孩子比較,若是孩子更小,那麼他們互換,直到該元素位於某個葉子中或者比他的兩個孩子都小。
  • findMin操做:指向最小堆的最小元素。
    • 返回存在根處的元素。

用鏈表實現堆

  • 由於要求插入元素之後可以向上遍歷,因此堆中的結點必須存儲指向雙親的指針。繼承BinaryTreeNode類,而且添加雙親指針和對應的set方法。
  • 有一個實例數據lastNode做用是跟蹤記錄堆中的最後一個葉子,也就是指向末結點的引用。
    算法

  • addElement操做:
    • 在適當位置添加一個元素。
    • 對堆進行重排序,以保持其有序屬性。
    • lastNode指針從新設定爲指向新的最末結點。
    • 在最壞的狀況下,肯定要插入結點的雙親,須要從堆的右下結點往上遍歷到根,而後往下遍歷到堆的左下結點。時間複雜度爲2 * logn。插入新結點,簡單賦值,時間複雜度爲O(1)。若是須要重排序,最多須要比較logn次,由於路徑最長爲logn。因此addElement操做的複雜度爲2 * logn + 1 + logn,爲O(logn)
  • removeMin操做:
    • 用存儲在最末結點處的元素替換存儲在根處的元素。
    • 對堆進行重排序。
    • 返回原來的根元素。
    • 在最壞的狀況下,替換結點,簡單賦值,時間複雜度爲O(1),而後重排序,,由於路徑最長爲logn,因此仍是比較logn次。肯定新的最末結點,從葉子到根的遍歷,再從根到另外一個葉子的遍歷。因此removeMin操做的複雜度爲2 * logn + logn + 1,爲O(logn)
  • findMin操做
    • 直接返回根元素,複雜度爲O(1)

用數組實現堆

  • 樹的根位於位置0處,對於每一結點n,n的左孩子將位於數組的2n+1位置處,n的右孩子將位於數組的2(n+1)位置處。
  • addElement操做:
    • 在恰當位置處添加新結點。
    • 對堆進行重排序以維持其排序屬性。
    • 將count值遞增1。
    • 時間複雜度爲 1 + log ,爲 O(logn)。
  • removeMin操做
    • 用存儲在最末元素處的元素替換存儲在根處的元素。
    • 對堆進行重排序。
    • 返回初始的根元素,並將count值減1。
    • 時間複雜度爲 1 + log ,爲 O(logn)。
  • findMin操做
    • 指向索引爲0,時間複雜度爲O(1)

使用堆:優先級隊列

  • 遵循兩個排序規則:
    • 具備更高優先級的項目在先。
    • 具備相同優先級的項目使用先進先出方法來肯定順序。
  • 雖然最小堆根本就不是一個隊列,可是它卻提供了一個高效的優先級隊列實現。

使用堆:堆排序

  • 原理:根據堆的有序屬性,將列表的每個元素添加到堆中,而後一次一個的把他們從根中刪除。
  • 堆排序是一種選擇排序,總體主要由構建初始堆+交換堆頂元素和末尾元素並重建堆兩部分組成。其中構建初始堆經推導複雜度爲O(n),在交換並重建堆的過程當中,需交換n-1次,而重建堆的過程當中,根據徹底二叉樹的性質,[log2(n-1),log2(n-2)...1]逐步遞減,近似爲nlogn。因此堆排序時間複雜度通常認爲就是O(nlogn)。
  • 堆排序時間複雜度O(nlogn)。
  • 步驟:
    • 步驟一:構造初始堆。將給定無序序列構形成一個堆(升序採用小頂堆,降序採用大頂堆)。
    • 步驟二:將堆頂元素與末尾元素進行交換,使末尾元素最大。而後繼續調整堆,再將堆頂元素與末尾元素交換,獲得第二大元素。如此反覆進行交換、重建、交換。

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

  • 問題1:鏈表堆和數組堆的優缺點。
  • 問題1解決方案:
    • 在以前章節的樹的實現中,咱們大多數用的都是鏈表實現的。主要是由於,用數組實現樹的時候,可能會由於沒有左右孩子而浪費了大量的空間。可是,在堆的實現中,由於考慮其向上遍歷的特殊操做,咱們須要其雙親結點 。而又由於堆是一個徹底樹,因此,不會存在大量浪費空間的狀況 。因此,針對堆來講,數組實現的效率更高。
    • 根據書P268 和 P270,api

      由於數組不須要肯定新結點雙親的步驟,以及數組不須要肯定新的最末結點。因此,雖然他的時間複雜度和用鏈表實現時是同樣的,可是數組實現的效率更高一些。數組

    • 一樣,咱們沒必要拘泥於一種結構,一種實現方式。日常編寫代碼時也要注意其效率,代碼的相關優化和美觀。不要只把實現和完成任務當成標準。這是我之後也應該注意的。
  • 問題2:對於課上將的堆排序,還有課上的堆排序實踐沒有熟練掌握。重點概括記憶一下。
  • 問題2解決方案:
    • 首先,要清楚堆排序的思想,堆排序是一種選擇排序 。如何將一個雜亂排序的堆從新構形成最大堆,它的主要思路就是數據結構

      從上往下,將父節點與子節點以此比較。若是父節點最大則進行下一步循環,若是子節點更大,則將子節點與父節點位置互換,並進行下一步循環。注意父節點要與兩個子節點都進行比較。學習

    • 咱們第一步就應該明白,如何將一個無序列表構建成最大堆。優化

      從最後一個非葉節點開始調整。.net

    • 如圖,這裏的最後一個非葉子結點是結點4,那麼咱們就從這裏進行調整。設計

      每次調整都是從父節點、左孩子節點、右孩子節點三者中選擇最大者跟父節點進行交換(交換以後可能形成被交換的孩子節點不知足堆的性質,所以每次交換以後要從新對被交換的孩子節點進行調整)。

    • 如上圖,這裏從結點2開始作調整。左孩子爲結點4,右孩子爲結點5,將其與父結點作比較,發現左孩子比父結點更大。所以將它們作交換,設結點4爲最大的結點,並繼續以結點4開始作下一步運算。
    • 當構建好一個堆以後,咱們開始進行排序。將堆頂元素與末尾元素進行交換,使末尾元素最大。而後從新調整堆,一直到最後整個堆都不存在了,那麼數組就是有序的。

代碼調試中的問題和解決過程

  • 問題1:在實現PP12.1的時候,發現使用remove操做以後,雖然刪除了第一個進入的元素,可是又添加了一個元素。

如圖,發現,我插入隊列的依次是,2,10,3。而後中序遍歷就是10,2,3。這是沒有問題的,可是刪除以後,雖然2刪除掉了,可是多了一個3。

  • 問題1解決方案:
    • 這個問題多是由於我dequeue()方法,調用了以前父類ArrayHeap類中removeMin()方法所引發的。
    • 因此就不得不說一下我PP12.1的實現思路。實際上,隊列和優先級隊列必定程度上是同樣的。只是CompareTo方法的斷定條件不一樣。隊列只須要將進入堆的順序記錄下來就能夠而後比較便可。那麼這裏的調用的removeMin()方法實際上就是刪除隊列中順序最低的,也就是第一個進來的元素。
    public T removeMin() throws EmptyCollectionException 
    {
        if (isEmpty())
            throw new EmptyCollectionException("ArrayHeap");
    
        T minElement = tree[0];
        tree[0] = tree[count-1];
        heapifyRemove();
        count--;
    modCount--;
        return minElement;
    }
    • 代碼如上所示,能夠發現,在把數組的最後一個賦給第一個,重排序以後呢,並無對其進行清空操做。因此,存在會有兩個3的出現。那麼我只須要在以後添加一串
    tree[count] =null;
    就能夠解決問題了。

代碼託管

上週考試錯題總結

  • 在二叉查找樹刪除結點時,若是他有孩子,那麼讓他的孩子代替他。應該是升級而不是降級。

結對及互評

點評過的同窗博客和代碼

  • 上週博客互評狀況
    • 20172304
    • 段志軒同窗的博客是不斷進步的,仍是應該多多基於書本,務實基礎。在代碼實現方面不要急躁,耐心調試,確定能夠發現錯誤。
    • 20172328
    • 博客內容豐富,而且問題裏討論了上學期研究的棧內存和堆內存之間的區別。優秀。

其餘

堆是基於二叉樹的。這周看代碼的效率並非很高,對於代碼的理解也不是很深刻。歸結於學習積極性不高。凡事不要鑽牛角尖,過去的就過去了,一切都會好起來。不要給本身額外的壓力和負擔。
看過別的同窗優秀的博客之後發現本身不能懈怠。好比不少同窗都聯繫到了上學期的堆棧內存時的學習內容。學習應該有體系,不可以東拼西揍,撿西瓜丟芝麻。仍是要踏實下來,戒驕戒躁。

學習進度條

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積) 重要成長
目標 5000行 30篇 400小時
第一週 0/0 1/1 10/10
第二週 610/610 1/2 20/30
第三週 593/1230 1/3 18/48
第四周 2011/3241 2/5 30/78
第五週 956/4197 1/6 22/100
第六週 2294/6491 2/8 20/120
第七週 914/7405 1/9 20/140
第八週 2366/9771 2/11 22/162

參考資料

相關文章
相關標籤/搜索