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

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

教材學習內容總結

    • 堆(heap)就是具備兩個附加屬性的一棵二叉樹。特色:①最小堆是一棵徹底樹。②最小堆對每一結點,它小於或等於其左孩子和右孩子。(最大堆的結點大於或等於它的左右孩子)
    • HeapADT的UML描述。
操做 說明
addElement 將給定元素添加到該堆中 
removeMin 刪除堆的最小元素 
findMin 返回一個指向堆中最小元素的引用 
  • 最小堆結點的子樹一樣是最小堆,兩個不一樣的最小堆可能包含數據相同
  • addElement操做:將給定的元素添加到堆中的恰當位置,由堆的定義可知,這裏的元素要可以進行比較,因此必須是Comparable類型的,若是不是,則會拋出異常。若是一棵二叉樹是平衡的,即全部葉子都位於h或h-1層。其中h爲log2^n,且n是樹中的元素數目,由於堆是一棵徹底樹,因此h層的葉子都位於該樹的左邊。因此插入結點時只有兩種狀況:①h層的最後一個位置。②h+1層的第一個位置。將新元素添加至堆的末尾後,保持樹是徹底樹,將該元素向根的地方移動,將它與父結點對換,直到其中的元素大小關係知足要求爲止。
    • 在鏈表實現中,添加元素時首先要肯定插入結點的雙親。最壞的一種狀況是從右下的最後一個葉子節點一直遍歷到根,在遍歷到堆的左下結點 。該過程的時間複雜度爲2logn。下一步是插入節點(簡單的賦值,這裏的時間複雜度爲O(1))。最後一步是將這棵樹進行從新排序。由於從根到結點的路徑長度爲logn,因此最多須要進行logn此操做。所以使用鏈表實現時操做的複雜度爲2*logn+1+logn。即O(logn)
    • 在數組實現中,添加元素時並不須要肯定新結點雙親的步驟,可是,其餘兩個步驟與鏈表實現的同樣。所以,數組實現的addElement操做的時間複雜度爲1+logn或O(logn)。雖然這二者實現的複雜度相同,但數組實現的效率更高一些。

  • removeMin操做:根據堆的定義可知,堆中的最小元素位於根結點,這時咱們要把該樹儲存在最末一片葉子中的元素移到根結點處,一旦元素移動,就要對該樹進行從新排序:將該新根元素與較小的那個孩子進行比較,若是孩子更小則將它們互換。從上至下繼續這一過程,直到該元素要麼變成葉子結點,要麼比它的兩個孩子都小。
    • 在鏈表實現中,removeMin必須刪除根元素,並用最後一個結點的元素來替換它(簡單的賦值,時間複雜度爲O(1))。下面要對該樹進行從新排序,由於該樹原先是一個堆,因此只須要跟較小的一邊進行比較排序。由於從根到葉子的最大路徑長度爲logn,所以該步驟的時間複雜度爲O(logn)。到此時,這棵樹已經完成了,但在實際進行的過程當中,爲了繼續完成接下來的操做,咱們還要找到新的最末結點,最壞的狀況是進行叢葉子到根的遍歷,而後再從根往下到另外一葉子的遍歷。所以,該步驟的時間複雜度爲2logn。因而removeMin操做最後的時間複雜度爲2logn+logn+1,即O(logn)。
    • 在數組實現中,removeMin也像鏈表實現的那樣,只不過它不須要肯定最新的最末結點。所以,數組實現的removeMin操做的複雜度爲logn+1。即O(logn)。
  • findMin操做:findMin操做較爲簡單,由堆的定義可知,直接返回根結點的元素便可。
  • 堆和二叉排序樹的區別:
    • ①堆是一棵徹底二叉樹,二叉排序樹不必定是徹底二叉樹;
    • ②在二叉排序樹中,某結點的右孩子結點的值必定大於該結點的左孩子結點的值,在堆中卻不必定;
    • ③在二叉排序樹中,最小值結點是最左下結點,最大值結點是最右下結點。在堆中卻不必定;
  • 使用堆:優先級隊列:按照優先級從大到小進行排序,具備相同優先級的按照先進先出來進行排序。雖然最小堆根本就不是一個隊列,可是它卻提供了一個高效的優先級隊列實現。
操做 說明
addElement 往樹中添加一個元素 
removeElement 從樹中刪除一個元素 
removeAllOccurrences 從樹中刪除所指定元素的任何存在 
removeMin 刪除樹中的最小元素 
removeMax 刪除樹中的最大元素 
findMin 返回一個指向樹中最小元素的引用 
findMax 返回一個指向樹中最大元素的引用 
  • 用鏈表實現二叉查找樹:每一個BinaryTreeNode對象要維護一個指向結點所存儲元素的引用,另外還要維護指向結點的每一個孩子的引用。

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

  • 問題一:在使用數組實現最小堆時,若是要進行removeMin操做須要把根元素刪去,把「count-1」處的元素放到索引爲0的地方,那麼「count-1」處的元素不是還放在原位嗎?書上並無對該位置進行任何操做。
  • 問題一解決方案:當時看到這裏的時候,我認爲是刪除元素時就不對「count-1」處的元素進行操做,由於刪除元素後count變量會自減,到時候遍歷的時候只遍歷到count的位置,這個問題也就先放到這裏了。可是當我作到pp12_8時卻發現不是這樣。當我刪除了最小元素後,在遍歷的時候依舊會把最後一個元素打印。以下圖所示

      咱們能夠很清楚的看到,5做爲最後一個元素並無被刪除,仍然會被打印,因爲ArrayHeap繼承的是ARRayBinaryTree類,我又查看了ArrayBinaryTree中的遍歷方法。
public String toString() 
    {
        ArrayUnorderedList<T> tempList = new ArrayUnorderedList<T>();
        inOrder(0, tempList);

        return tempList.toString();
    }

  使用數組實現堆時使用中序遍歷,打印數組中不爲空的全部元素,因此在這裏我以爲書上的內容是有問題的,在ArrayHeap的removeMin()方法中應該添加吧「count-1」處的元素清空的操做。這樣問題就得以解決了。html

public T removeMin() throws EmptyCollectionException
    {
        if (isEmpty())
            throw new EmptyCollectionException("ArrayHeap");

        T minElement = tree[0];
        tree[0] = tree[count-1];
        tree[count-1] = null;(新添加的操做)
        heapifyRemove();
        count--;
        modCount--;
        
        return minElement;
    }

  • 問題二:在看鏈表實現堆時,有這樣一段話「heapifyAdd方法並無執行雙親與孩子的完整互換。它只是把雙親元素往下平移到正確的插入點,而後把新值賦給該物質。這並無真正改變算法的複雜度,即便執行完整的元素互換,其複雜度也是O(logn)。可是,它的確提升了效率,由於它減小了左堆的每一層上要執行的賦值次數。」我對於這段話不是很理解。
  • 問題一解決方案:我是這樣理解的,先用temp儲存插入節點的元素,與父結點內儲存的元素進行比較,若是父結點的元素大於temp,則把父結點的元素賦給子結點。注意:此時並無把temp的值賦給父結點,沒有執行完整的元素互換。這時繼續進行遍歷,找到父結點的父結點,讓父結點的父結點的元素與temp進行比較,重複以上步驟,直到根結點或有一結點的元素小於temp,這時,該結點的子結點就是新插入元素的位置,即把temp賦給它。因爲沒有進行完成的元素互換,效率天然也就提升了。

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

  • 問題1:對使用數組實現堆中的removeMin操做的heapifyRemove方法不是特別理解。代碼以下:
private void heapifyRemove()
    {
        T temp;
        int node = 0;
        int left = 1;
        int right = 2;
        int next;
        
        if ((tree[left] == null) && (tree[right] == null))
            next = count;
        else if (tree[right] == null)
            next = left;
        else if (((Comparable)tree[left]).compareTo(tree[right]) < 0)
            next = left;
        else
            next = right;
        temp = tree[node];

        while ((next < count) && 
            (((Comparable)tree[next]).compareTo(temp) < 0))
        {
            tree[node] = tree[next];
            node = next;
            left = 2 * node + 1;
            right = 2 * (node + 1);
            if ((tree[left] == null) && (tree[right] == null))
                next = count;
            else if (tree[right] == null)
                next = left;
            else if (((Comparable)tree[left]).compareTo(tree[right]) < 0)
                next = left;
            else
                next = right;
        }
        tree[node] = temp;
    }
  • 問題一解決方案:咱們先來一行一行的逐步分析代碼,咱們知道此方法是用來對堆進行從新排序的。首先,判斷刪除最小元素後是否只剩下一個結點,若是是,則跳事後面判斷和循環,將剩下的惟一元素設置爲根結點。有前面的內容可知,對於每一結點,n的右孩子將位於素組的2(n+1)處,n的右孩子將位於數組的2(n+1)處。若是對於根結點來講,它沒有右孩子或左孩子的元素小於右孩子的元素,則令next=left進入下一步的循環,若是它的右孩子的元素大於左孩子的元素。則令next=right進入下一步的循環。(注意:在判斷結束後,temp= tree[node]的做用爲保存此時根節點的元素向下遍歷進行比較,最後賦值給經過循環找到的結點。)當進入循環之後,將根結點的元素與索引爲next處的元素(左孩子或右孩子)進行比較,若是next處的元素比temp小,則令node(next的父結點)處元素爲next。繼續向下遍歷,把next的值賦給node,並找到node的孩子的索引,令next從新指向node的孩子。當跳出循環時,node結點的位置即爲temp的位置,完成排序。

代碼託管

上週考試錯題總結

  • 錯題1:What type does "compareTo" return?
    A . int
    B . String
    C . boolean
    D . char
  • 解析:這題錯的實在是不該該,當時作的太快了,滿腦子都想着compareTo用來比較兩個對象之間的大小,返回一個boolean型的變量,卻忘了是根據返回int值的大小來判斷的。
  • 錯題2:Insertion sort is an algorithm that sorts a list of values by repetitively putting a particular value into its final, sorted, position.
    A . true
    B . false
  • 解析:概念題,插入排序經過反覆地把某個元素插入到以前已排序的子列表中,實現元素的排序。
  • 錯題3:A binary search tree is a binary tree with the added property that the left child is greater than the parent, which is less than or equal to the right child.
    A . True
    B . Flase
  • 解析:概念題,作的時候理解錯誤,二叉查找樹是一種含有附加屬性的二叉樹,即其左孩子小於父結點,而父結點又小於或等於右孩子。
  • 錯題4:One of the uses of trees is to provide simpler implementations of other collections.
    A . True
    B . Flase
  • 解析:這道題作的時候沒有理解清楚題意。樹的主要做用之意2是爲其餘集合提供高效的實現,而不是簡單的實現。

結對及互評

  • 博客中值得學習的或問題:
    • 排版精美,對教材的總結細緻,善於發現問題,對於問題研究得很細緻,解答也很周全。
  • 代碼中值得學習的或問題:
    • 代碼寫的很規範,思路很清晰,繼續加油!

點評過的同窗博客和代碼

  • 本週結對學習狀況

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

  相較與上週的折磨,這周的內容上能夠說是友好了許多,經歷了上週二插查找樹的洗禮,這周的內容也只不過是新瓶裝舊酒而已,沒有什麼太過新鮮的東西,主要是把一些關鍵的代碼給弄明白,這一過程仍是花了很多時間的。因爲這周的實驗也佔用了很多時間,這周的學習內容其實並無想象中那麼輕鬆。但願本身能在之後的生活中繼續努力,不斷進步!java

學習進度條

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積) 重要成長
第一週 200/200 1/1 5/20
第二週 981/1181 1/2 15/20
第三週 1694/2875 1/3 15/35
第四周 3129/6004 1/4 15/50
第五週 1294/7298 1/5 15/65
第六週 1426/8724 1/6 20/85
第七週 2071/10795 1/7 20/105
第八週 3393/14188 1/8 20/125
  • 計劃學習時間:20小時node

  • 實際學習時間:20小時git

參考資料

相關文章
相關標籤/搜索