1. 程式人生 > >20172306 2018-2019-2 《Java程序設計與數據結構》第八周學習總結

20172306 2018-2019-2 《Java程序設計與數據結構》第八周學習總結

class 平衡 each 插入 二叉 情況下 mov com 插入元素

20172306 2018-2019-2 《Java程序設計與數據結構》第八周學習總結

https://img2018.cnblogs.com/blog/1333004/201811/1333004-20181110144559856-530051268.gif

教材學習內容總結

    • 堆是具有兩個附加屬性的一棵二叉樹
      • 它是一個完全樹
      • 對每一結點,它小於或等於其左孩子和右孩子(這個描述的是最小堆)
      • 一個堆也可以是最大堆,其中的結點大於或等於它的左右孩子
      • 它繼承了二叉樹的所有操作
        技術分享圖片
    • addElement操作
      • 如果給定元素不是Comparable的,則該方法將拋出一個ClassCastException異常
      • addElement方法將給定的Comparable元素添加到堆中的恰當位置處,且維持該堆的完全性屬性和有序屬性。
      • 因為一個堆就是一棵完全樹,所以對於插入的新結點而言,只存在一個正確的位置,要麽就是葉子不在一層,那麽就取最後一層的空處;要麽就是在同一層,就在它的左面的第一個位置。在插入後,就會對它的完整性和有序性進行改變,所以就要進行重新的排序,拿該值和雙親結點進行比較,然後進行互換,然後沿著樹向上繼續找,直到到正確的位置。
      • 通常,在堆實現中,我們會對樹中的最末一個結點,或更為準確的是,最末一片葉子進行跟蹤記錄。
    • 例如下面的過程(我們要插入的是0,因為插入後,不是最小堆,不符合規定,所以我們進行重排序):

技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

  • removeMin操作
    • 對於最小堆來說,Min就是根的位置的元素。所以我們在這個方法上就是將根元素刪掉,然後再進行其他操作。
    • 對於這個堆來說,完整性和平衡性是很重要的,所以為了維持該樹的完全性,只有一個能替換根的合法元素,且它是存儲在樹中最末一片葉子上的元素。對於最末一片葉子來說,就是在樹最後一層最右邊的葉子。
    • 對於addElement來說,我們在添加的時候,有可能違背最小堆的特性,同樣,對於removeMin來說,也會出現這樣的問題,所以我們要進行重排序以維持其原有的屬性。過程主要是將替換後的元素和它的孩子進行比較,然後依次向下,直到最後的一層,形成最小堆截止。
    • 例如下面的過程:
      技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

  • findMin操作
    • findMin就很好理解了,因為Min就是最根部的元素,所以就將返回為root就可以了。
  • 使用堆:優先級隊列
    • 優先級隊列(priority queue):就是遵循兩個排序規則的集合,首先,具有更高優先級的項目優先;其次,具有相同優先級的項目使用先進先出方法來確定其排序。
    • 雖然最小堆根本不是一個隊列,但是它卻提供了一個高效的優先級隊列實現。
    • 我們利用堆來實現一個優先級隊列:我們要實現以上的兩個排序規則
      • 首先,我們使用一個最小堆,需要將高優先級的項目在先
      • 其次,我們要解決的是對相同優先級的進行先進先出的排序
        • 1.創建一個PriorityQueueNode對象,存儲將被放置在隊列中的元素,該元素的優先級,以及元素放進隊列的順序
        public PrioritizedObject(T element, int priority)
          {
        this.element = element;//元素
        this.priority = priority;//優先級
        arrivalOrder = nextOrder;//放入隊列的順序
        nextOrder++;
          }
        • 2.為PriorityQueueNode類定義一個CompareTo方法,來完成優先級相同時的比較
        public int compareTo(PrioritizedObject obj)
          {
        int result;
        
        if (priority > obj.getPriority())
            result = 1;
        else if (priority < obj.getPriority())
            result = -1;
        else if (arrivalOrder > obj.getArrivalOrder())
            result = 1;
        else
            result = -1;
        
        return result;
          }
  • 鏈表實現和數組實現的addElement操作的時間復雜度同為o(logn)
  • 鏈表實現和數組實現的removeMin操作的復雜度同為o(logn)

  • 用鏈表實現堆
    • 堆是二叉樹的一種擴展,所以在插入元素後仍舊能夠向上遍歷該樹,所以堆中的結點必須存儲指向其雙親的指針。
    • 鏈表實現的實例數據由指向HeapNode且稱為lastNode的單個引用組成,這樣我們就能夠跟蹤記錄該堆中的最末一片葉子
    public HeapNode lastNode;
    • addElement操作
      • 達到3個目的:在恰當位置處添加一個新的元素;對堆進行重排序以維持排序屬性;將lastNode指針重新設定為指向新的最末結點
      • 其使用了兩個私有方法
        • getNextParentAdd:它返回一個指向某結點的引用,該結點為插入結點的雙親
        • heapifyAdd:完成對堆的任何重排序,從那片新葉子開始向上處理至根處
      • 添加元素對於復雜度(復雜度為:2*logn + 1 +logn,即o(logn)):
        • 第一步是確定要插入結點的雙親,在最壞情況下,要從堆的右下結點往上遍歷到根,然後再向下到堆的左下結點,時間復雜度為2*logn
        • 下一步是插入新結點,時間復雜度為常量
        • 最後一步是重排序,最多需要logn次操作
        • heapifyAdd未執行雙親與孩子的完整互換,只是把雙親元素向下平移到正確的插入點,然後把新值賦給該位置。但它提高了效率,因為減少了在堆的每一層上要執行的賦值次數。
    • removeMin 操作
      • 達到3個目的:用存儲在最末結點處的元素替換存儲在根處的元素;對堆重排序;返回初始的根元素。
      • 其使用了兩個私有方法
        • getNewLastNode:它返回一個指向某一結點的引用,該結點是新的最末結點
        • heapifyRemove:進行重排序(從根向下)
      • 刪除根元素對於復雜度(復雜度為:2*logn + logn + 1,即o(logn))
        • 第一步是替換元素,其時間復雜度為常量
        • 第二步是重排序(根往下到葉子),時間復雜度為logn
        • 第三部為確定新的最末結點,和之前相同為2*logn
    • findMin操作
      • 該元素在堆根處,只需返回根處即可
      • 復雜度為o(1)
  • 用數組實現堆
    • 在二叉樹的數組實現中,樹的根位於位置0處,對於每一結點n,n的左孩子將位於數組的2n+1位置處,n的右孩子將位於數組的2(n+1)位置處

    • addElement操作
      • 達到3個目的:在恰當位置處添加新結點;對堆進行重排序;將count遞增1(我認為原因是數組是有容量的,元素增多,要給它空間)
      • 其中有一個私有方法
        • heapifyAdd:進行重排序
      • 添加元素對於復雜度(復雜度為:1+logn,即o(logn))
        • 不需要確定新結點的雙親,因為會自動根據位置進行放,但是 其他的步驟和鏈表相同。
        • 數組實現的效率更高些,但復雜度相同為o(logn)
    • removeMin操作
      • 達到3個目的:用存儲在最末元素處的元素替換存儲在根處的元素;對堆進行重排序;返回初始的根元素
      • 其中有一個私有方法
        • 最末一個元素是存儲在數組的count-1位置處的(count是所有的元素值)
        • heapifyRemove:進行重排序
      • 刪除元素對於復雜度(復雜度為:logn+1,即o(logn))
        • 不需要確定新的最末結點
    • findMin操作
      • 該元素為在根處,所以在數組的0位置處
      • 復雜度為o(1)
  • 使用堆:堆排序老師上課用的PPT,我覺得上面的堆排序過程很詳細,而且易懂
    • heapSort方法的兩部分構成:添加列表的每個元素,然後一次刪除一個元素

    • 就堆排序來說,老師在上課的時候講的很詳細,而且因為它是有最小堆和最大堆的,根部要不是最小元素,要不是最大元素,因此,堆排序的關鍵就是通過不斷的移動,將最值放在根處,然後利用remove根處元素的方法,將元素刪除出來,再對剩余的堆進行重排序,然後繼續刪除根部,而這個過程,每刪除的元素排列出來就是我們所需要的升序或者降序,即堆排序完成

    • 堆排序我們也可以用數組的知識進行理解。因為我們在數組實現堆時,已經知道了對於一個根結點,其左右孩子的確定所在位置,所以我們可以利用比較和互換,進行排序,其實原理和上一條是相同的。

    • 無論是用列表還是數組進行堆排序,他們的復雜度相同,都是o(nlogn)
      技術分享圖片

      • 對於列表進行堆排序來說:在前面的學習中,我們知道,無論是addElement還是removeMin它們的復雜度都是o(logn)。但是我們要註意的是,這個復雜度是在添加或者刪除一個元素的情況下的復雜度。而我們進行排序的時候,是需要添加且刪除n個元素的,因此,我們分別要進行這兩個操作n次,以滿足對n個元素進行排序。所以最終復雜度為2nlogn,即o(nlogn)

      • 對於數組進行堆排序來說:從數組中第一個非葉子的結點開始,將它與其孩子進行比較和互換(如有必要)。然後在數組中往後操作,直到根。對每個非葉子的結點,最多要求進行兩次比較操作,所以復雜度為o(n)。但是要使用這種方式從堆中刪除每個元素並維持堆的屬性時,其復雜度仍為o(nlogn)。因此,即使這種方式的效率稍好些,約等於2*n+logn,但其復雜度仍為o(nlogn)。

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

  • 問題1:書中對於優先級隊列的內容其實挺簡略的,所以我上網找一下優先級隊列的內容
  • 問題1解決方案:
    隊列就是先進先出的一種形式,而優先級實際上就是根據某種標準進行排序,高級的就先排,對於相同級別的就根據先進先出的隊列的要求進行排序,優先級隊列也叫優先權隊列。
    對於優先級隊列的特點:
    1.優先級隊列是0個或多個元素的集合,每個元素都有一個優先權或值。
    2.當給每個元素分配一個數字來標記其優先級時,可設較小的數字具有較高的優先級,這樣更方便地在一個集合中訪問優先級最高的元素,並對其進行查找和刪除操作。
    3.對優先級隊列,執行的操作主要有:(1)查找,(2)插入,(3)刪除。
    4.在最小優先級隊列中,查找操作用來搜索優先權最小的元素,刪除操作用來刪除該元素。
    5.在最大優先級隊列中,查找操作用來搜索優先權最大的元素,刪除操作用來刪除該元素。
    註:我們在刪除之後,要根據要求對之後的元素進行重新的排列,這個時候,我們可能出現多種的相同優先權,所以,這個時候就應該根據隊列的要素,進行先進先出的進行排序,因此這也提醒我們,我們在寫代碼時,我們要對進入隊列的順序進行記錄。
    6.插入操作均只是簡單地把一個新的元素加入到隊列中。

  • 問題2:我和曾程在宿舍看書的時候,看到getNextParentAdd這段代碼不理解,我倆都處於有點蒙圈的狀態。
 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解決方案:後來晚上聽馨雨和仇夏討論的時候,我倆也去聽了下,就懂得了。
    下面是一個自己畫的圖的過程:
    技術分享圖片

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

  • 問題1:在進行ArrayHeap的測試時,出現這樣的情況,它刪除之後,後面總是多出來一個數字,不知道為什麽?
    技術分享圖片

  • 問題1解決方案:後來我問了譚鑫,譚鑫說,書中的removeMin少寫了一條語句,就是
tree[count - 1] = null;

然後我把它加上就正常了
技術分享圖片
後來他給我講原因:

T minElement = tree[0];//最小的元素是根結點,也就是數組的0位置處
        tree[0] = tree[count-1];//這個時候,將最末葉子結點放在了根結點處,準備進行重排序
        tree[count-1] = null ;//將放在上面的那個最末葉子結點去掉,否則就會多出來
        heapifyRemove();//進行重排序
  • 問題2:
  • 問題2解決方案:
  • ...

代碼托管

(statistics.sh腳本的運行結果截圖)

上周考試錯題總結

  • 技術分享圖片
  • 這個是馬虎的,我後來看IDEA知道了compareTo返回的是boolean型

  • 技術分享圖片
  • 選擇排序算法通過反復地將某一特定值放在它的列表中的最終已排序位置從而完成對某一列表值的排序

  • 技術分享圖片
  • 插入排序算法通過反復地將某一特定值插入到該列表某個已排序的子集中來完成對列表值的排序

結對及互評

評分標準

點評模板:

  • 博客中值得學習的或問題:
    • 課本內容總結的很詳細
  • 代碼中值得學習的或問題:
    • 有的問題我沒遇到過,看到他的也算是種收獲

點評過的同學博客和代碼

  • 本周結對學習情況
    • 20172325
    • 結對學習內容
      • 一起學習了十二章的內容
      • 共同學習了堆的排序
      • 討論了堆的添加和刪除

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

第十二章老師講的比較簡略,主要講了堆排序的內容,說是很有用的,而且我也聽懂了,哈哈哈!

學習進度條

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積) 重要成長
目標 5000行 30篇 400小時
第一周 0/0 1/1 6/6
第二周 985/985 1/1 18/24
第三周 663/1648 1/1 16/40
第四周 1742 /3390 2/2 44/84
第五周 933/4323 1/1 23/107
第六周 1110/5433 2/2 44/151
第七周 1536/6969 1/1 56/207
第八周 / 2/2 60/267

參考資料

  • 堆的插入和刪除分析
  • 鏈式結構反應堆
  • 優先級隊列

20172306 2018-2019-2 《Java程序設計與數據結構》第八周學習總結