20172314 2018-2019-1《程序設計與數據結構》第八週學習總結

教材學習內容總結

  • 堆:是一顆徹底二叉樹,每個結點都小於等於左右孩子(最小堆),或大於等於左右孩子(最大堆)他的每個子樹也是最小堆或最大堆。
  • 堆的操做
  • addElement操做(堆的插入)
    • 插入新結點時,是做爲葉子結點插入的,且必定要保持樹的完整性。若是最下層不滿,插入到左邊的下一個空位,若是最底層是滿的,插入到新一層的左邊第一個位置。
    • 若是插入的數相對樹而言較小,則須要在插入後進行重排序
  • removeMin方法(堆的刪除)
    • 因爲最小元素儲存在根處,爲了維持樹的完整性,那麼只有一個能替換根的合法元素,且它是存儲在樹中最末一片葉子上的元素(將是樹中h層上最右邊的葉子)。
    • 將替換元素移到根部後,必須對該堆進行重排序。
  • findMin操做
    • 這個方法須要返回在最小堆中最小元素的引用,因爲最小堆中最小元素在根處,因此只需返回根部元素便可。

使用堆:優先級隊列

  • 具備更高優先級的項目在先。
  • 具備相同優先級的項目使用先進先出的方法來肯定其排序。
  • 雖然最小堆不是一個隊列,但它提供了一個高效的優先級隊列實現。

用鏈表實現堆

  • 因爲要求插入元素後可以向上遍歷樹,因此堆結點中必須存儲指向雙親的指針。
  • 因爲BinaryTreeNode類沒有雙親指針,因此要建立一個HeapNode類開始鏈表實現。
    • addElement操做
      • 在適當位置添加一個新元素
      • 對堆進行重排序以維持其排序屬性
      • 將lastNode指針從新設定爲指向新的最末結點
      • 在這個方法中用到了getNextParentAdd方法(返回指向某結點的引用,該結點爲插入結點的雙親),還有一個heapifyAdd方法,(完成對堆的任何重排序,重新葉子開始向上處理至根處)
      • addElement操做有三步
        • 首先要肯定插入結點的雙親(從堆的右下結點往上遍歷到根,再往下遍歷到左下結點,時間複雜度爲2*logn)
        • 其次是插入新結點(只涉及簡單的賦值語句,時間複雜度爲O(1))
        • 最後是從被插入葉子處到根處的路徑重排序,來保證堆的性質不被破壞(路徑長度爲logn,最多須要logn次比較)
        • 所以addElement操做的複雜度爲2*logn+1+logn,即爲O(logn)
    • removeMin操做
      • 用最末結點處的元素替換根處元素
      • 對堆進行重排序
      • 返回初始的根元素

用數組實現堆

  • 數組相比於鏈表,優勢是不須要遍從來查找最末一片葉子或下一個插入結點的雙親,直接查看數組的最末一個元素便可。
  • 在數組中,樹根存放在0處,對於每個結點n,n的左孩子位於數組2n+1處,n的右孩子位於數組的2(n+1)處。一樣,反過來查找時,對於任何除了根以外的結點,n的雙親位於(n-1)/2位置處。
  • addElement操做
    • 在恰當位置處添加新結點
    • 對堆進行重排序以維持其屬性
    • 將count遞增1
    • 只須要一個heapifyAdd方法進行重排序,不須要從新肯定新結點雙親位置。此方法的時間複雜度是O(logn)
  • removeMin操做
    • 用最末元素替換根元素
    • 對堆進行重排序
    • 返回初始根元素
    • 因爲末結點存儲在數組的count-1位置處,因此不須要肯定最新末結點的方法,此方法複雜度爲O(logn)
  • findMin操做
    僅僅返回根處元素便可,就是數組的位置0處,複雜度爲O(1)

使用堆:堆排序

  • 堆排序的過程(由兩部分構成,添加和刪除)
    • 將列表的每一元素添加到堆中
    • 一次一個將他們從根刪除,再次存放到數組中
    • 最小堆時獲得的排序結果是升序,最大堆時獲得的排序結果是降序
    • 對於每個結點來講,添加和刪除操做的複雜度都是O(logn),因此對於有n個結點的堆來講,複雜度是O(nlogn)
  • 堆排序的複雜度具體分析
    • 堆化過程通常是用父節點和他的孩子節點進行比較,取最大的孩子節點和其進行交換;可是要注意這應該是個逆序的,先排序好子樹的順序,而後再一步步往上,到排序根節點上。而後又相反(由於根節點也多是很小的)的,從根節點往子樹上排序。最後才能把全部元素排序好。對於每一個非葉子的結點,最多進行兩次比較操做(與兩個孩子)所以複雜度爲2*n
    • 排序過程就是把根元素從堆中刪除,刪除每一個元素複雜度爲logn,對全部元素來講,複雜度爲nlogn。
    • 因此是2*n + nlogn,複雜度是O(nlogn)

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

  • 問題一:getNextParentAdd方法的代碼理解
  • 問題一解決:
    * 第一種狀況

    首先考慮while循環,result爲8,他是左孩子,因此不進入while循環,接着往下,他不是根結點,他不是父結點的右孩子,因此result爲父結點5,即返回的插入結點的雙親。
    * 第二種狀況

    result爲0,雙親結點的左孩子爲-2,因此進入while循環,result爲3,接下來進入if語句,他不是根結點,並且父結點的右孩子(1)不爲空,因此返回的插入結點的雙親結點是1
    * 第三種狀況

    while循環中,result(2)不是左孩子,因此result爲雙親結點1,往下,他是根結點,並且左子樹不爲空,result爲左孩子0.這種狀況就至關於滿樹時插入到新一層的左邊位置。html

    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;
    }
  • 問題二:heapifyAdd方法的代碼理解
  • 問題二解決:舉個例子,git

    private void heapifyAdd() {
        T temp;
        HeapNode<T> next = lastNode;
        temp = next.getElement();
        while ((next != root)
                && (((Comparable) temp)             .compareTo(next.getParent().getElement()) < 0)) {
            next.setElement(next.getParent().getElement());
            next = next.parent;
        }
        next.setElement(temp);
    }


    如圖在樹中插入結點2,那麼next指向2,temp=2,這時next不是根結點,而且2(temp)比4(next.getParent().getElement())小,因此如今next的值設置爲4(即原來2的那個位置變成4),next指向原來next的父親結點(即原來的4位置),而後將next的值設爲temp即2(原來4的位置變爲2),這樣就將2和4換位,實現了重排序。算法

  • 問題三:堆排序的過程
  • 問題三解決:首先把待排序的元素在二叉樹位置上排列,而後調整爲大頂堆或小頂堆,接下來進行排序,以小頂堆爲例,根結點爲最小,因此拿掉根,而後對堆進行調整,使其符合小頂堆的要求,而後新的堆頂又是最小的,將其拿掉,如此循環...藍墨雲關於這個的測試題,從新梳理一下如圖,大頂堆的過程與其相似,但注意在代碼實現中,小頂堆排序結果爲升序,大頂堆排序結果爲降序
    api

  • 問題四:對於優先級隊列的理解
  • 問題四解決:隊列是一種特徵爲FIFO的數據結構,每次從隊列中取出的是最先加入隊列中的元素。可是,許多應用須要另外一種隊列,每次從隊列中取出的應是具備最高優先權的元素,這種隊列就是優先級隊列。優先級隊列的特色:
    • 優先級隊列是0個或多個元素的集合,每一個元素都有一個優先權或值。
    • 當給每一個元素分配一個數字來標記其優先級時,可設較小的數字具備較高的優先級,這樣更方便地在一個集合中訪問優先級最高的元素,並對其進行查找和刪除操做。
    • 在最小優先級隊列(min Priority Queue)中,查找操做用來搜索優先級最小的元素,刪除操做用來刪除該元素。在最大優先級隊列(max Priority Queue)中,查找操做用來搜索優先級最大的元素,刪除操做用來刪除該元素。
    • 每一個元素的優先級根據問題的要求而定。當從優先級隊列中刪除一個元素時,可能出現多個元素具備相同的優先權。在這種狀況下,把這些具備相同優先權的元素按先進先出處理。

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

  • 問題一:在作藍墨雲測試構造大頂堆並排序時,對於過程的輸出結果是每次將最大數從堆頂拿下時的結果,並非顯示每次排序時的數組狀況。
    數組

  • 問題一解決:主要錯誤之處在於測試類,如圖中顯示,個人過程記錄是數據結構

    for(int i=0;i<8;i++){
        array2.addElement(array.removeMax);
        array2.removeMax();
        System.out.println("第"+i+"次爲:"+ array2);
    }

    首先,在循環中array中每次拿掉的最大數添加到array2中,而後輸出的是array2中的最大數,那麼就至關於每次把array中的最大數輸出,array2沒有什麼做用。應該爲學習

    for (int i = 0; i <a.length; i++) {
            array2[i]=array.removeMax();
            System.out.println("第"+ i +"次爲:"+ array);
        }
    這樣就把array2的做用發揮出來了,array2中存放array中依次彈出的最大數,在array2中降序排列,而後每次輸出array數組,顯示如今數組狀況,排序的結果就能夠用相似上面的for循環依次將array2中的數輸出。
  • 問題二:在作PP12.1時,對於優先級隊列的輸出狀況理解有問題。
  • 問題二解決:測試類經過在循環中使用queue.removeNext()方法,是因爲PriorityQueue類中removeNext方法繼承了ArrayHeap類中的removeMin方法,而在ArrayHeap類中,removeMin又使用了方法heapModifyRemove,其中測試

    else if (((Comparable)tree[left]).compareTo(tree[right]) < 0)
            next = left

    當left比right小時,返回左,我最開始不理解的是在優先級隊列中,當優先級相同時.net

    if (arrivalOrder >obj.getArrivalOrder())//優先級相同時,先進先出
          result = 1;

    應返回左邊的arrivalOrder,但在heapModifyRemove中來講,卻返回compareTo右邊的,因此我以爲矛盾,進行屢次測試後我發現輸出的結果沒有錯。又仔細看代碼以後發現,優先級相同時,返回1的狀況是arrivalOrder較大,那麼就表明它是後進的,並非跟優先級同樣大的先,arrivalOrder越大表明晚進,應該後出,因此正好反一下。3d

  • 問題三:實現ArrayHeap類時,發現輸出結果老是多一個數

  • 問題三解決:參考了譚鑫同窗的博客,裏面提到了解決辦法,如圖

    添加語句以後解決問題,緣由是在將根結點拿掉並用末葉子結點替換後並無將葉結點刪除,因此致使出現兩次,添加語句tree[count-1] = null;以後,將其值設置爲空,至關於刪除。

代碼託管

上週考試錯題總結

  • 錯題一:

  • 錯題一解決:CompareTo方法返回值是int型的,若是前者比後者大的話返回1,小的話返回-1,相等返回0.

結對及互評

  • 20172305譚鑫:譚鑫的博客中提到的」問題1:實現優先級隊列的二叉堆、d堆、左式堆、斜堆、二項堆都是什麼意思?和最小堆、最大堆有什麼區別?」是我沒有學習過的,學習到了左式堆,二項堆,斜堆等堆。同時參考他的博客解決了一個代碼問題,很是有價值。
  • 20172323王禹涵:王禹涵的博客對代碼的解釋很詳盡,排版精美,很溫馨。

其餘

這一章感受原理很容易理解,邏輯簡單,但用代碼實現很差理解,可是書上代碼的可用性很高,而後實驗方面也花費較長時間,總結出來就是要儘早去掌握核心代碼再開展學習。

學習進度條

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積)
目標 5000行 30篇 400小時
第一週 0/0 1/1 8/8
第二週 1163/1163 1/2 15/23
第三週 774/1937 1/3 12/50
第四周 3596/5569 2/5 12/62
第五週 3329/8898 2/7 12/74
第六週 4541/13439 3/10 12/86
第七週 1740/15179 1/11 12/97
第八週 5947/21126 1/12 12/109

參考:

相關文章
相關標籤/搜索