1. 程式人生 > >20172302 《Java軟體結構與資料結構》第七週學習總結

20172302 《Java軟體結構與資料結構》第七週學習總結


2018年學習總結部落格總目錄:第一週 第二週 第三週 第四周 第五週 第六週 第七週


教材學習內容總結

第11章 二叉查詢樹

1.二叉查詢樹是一種含有附加屬性的二叉樹,該屬性即其左孩子小於父節點,而父節點又小於等於其右孩子。二叉查詢樹的一個示意圖:

在二叉查詢樹中:
       (01) 若任意節點的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;
       (02) 任意節點的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;
       (03) 任意節點的左、右子樹也分別為二叉查詢樹。

2.二叉查詢樹的介面類繼承自二叉樹的介面類BinaryTreeADT,在其基礎上補充了一些操作方法,程式碼如下:

public interface BinarySearchTreeADT<T> extends BinaryTreeADT<T>
{
    public void addElement(T element);
    //往樹中新增一個元素
    public T removeElement(T targetElement);
    // 從樹中刪除一個元素
    public void removeAllOccurrences(T targetElement);
    // 從樹中刪除所指定元素的任何存在
    public T removeMin();
    //刪除樹中最小元素
    public T removeMax();
    // 刪除樹中最大元素
    public T findMin();
    //返回一個指向樹中最小元素的引用
    public T findMax();
    //返回一個指向樹中最大元素的引用
}

3.連結串列實現二叉查詢樹
       LinkedBinarySearchTree類它繼承自LinkedBinaryTree類,是對LinkedBinaryTree類的一種拓展,同時它還實現了BinarySearchTreeADT介面類。

  • 建構函式
    • 建立空的LinkedBinarySearchTree
    public LinkedBinarySearchTree()
    {
          super();
    }
    • 根結點為特定元素的LinkedBinarySearchTree
    public LinkedBinarySearchTree(T element)
      {
          super(element);
    
          if (!(element instanceof Comparable))
              throw new NonComparableElementException("LinkedBinarySearchTree");
      }
  • addElement操作
    1.樹空,新元素成為樹的根結點;
    2.非空,與樹根元素進行比較;
    (1)小於根元素:①左孩子為null:新元素成為根的左孩子;②左孩子不為null:遍歷根的左孩子,再次進行比較操作;
    (2)大於等於根元素:①右孩子為null:新元素成為根的右孩子;②右孩子不為null:遍歷根的右孩子,再次進行比較操作;

  • removeElement操作
           找到被刪除結點的替換結點,刪除被刪除結點,替換為替換結點。
           選擇替換結點的三種情況:
    (1)被刪除結點沒有孩子,replacement返回null;
    (2)被刪除結點有一個孩子,replacement返回這個孩子;
    (3)被刪除結點有兩個孩子,replacement返回中序後繼者;(處於根結點右子樹上)
    示意圖:

  • removeAllOccurrences操作
    刪除指定元素的所有存在,方法程式碼:
public void removeAllOccurrences(T targetElement)
            throws ElementNotFoundException
    {
        removeElement(targetElement);

        try
        {
            while (contains((T)targetElement))
                removeElement(targetElement);
        }

        catch (Exception ElementNotFoundException)
        {
        }
    }
  • removeMin操作
    最小元素在樹中位置的3種情形:
    (1)樹根沒有左孩子,樹根即為最小元素,樹根右孩子變成新的根結點;
    (2)樹的最左側結點為一片葉子,該葉子即為最小元素,設定其父結點的左孩子應用為null;
    (3)樹的最左側結點為內部結點,設定其父結點的左孩子引用指向最小元素的右孩子。

4.用有序列表實現二叉查詢樹
LinkedBinarySearchTree類有很多方法與有序列表的方法之間存在著一一對應關係

  • 平衡二叉查詢樹的假設下,有序連結串列與的連結串列實現分析和二叉查詢樹的實現分析。

           add和remove操作有可能使得二叉查詢樹不再平衡,那麼我們的二叉查詢樹的操作複雜度將會大大增加。

5.平衡二叉查詢樹
       如果二叉查詢樹不平衡,那麼其效率可能比線性結構還要低。接下來討論一些常用的平衡技術。
(1)右旋
使用條件:根結點左孩子的左子樹過長導致不平衡
使用方法:①樹根左孩子元素成為新的根元素②原樹根元素稱為新樹根的右孩子元素③使原樹根左孩子的右孩子,成為原樹根的新的左孩子

(2)左旋
使用條件:根結點右孩子的右子樹過長導致不平衡
使用方法:①樹根右孩子元素成為新的根元素②原樹根元素稱為新樹根的左孩子元素③使原樹根右孩子的左孩子,成為原樹根的新的右孩子

(3)右左旋
使用條件:根結點的右孩子的左子樹過長導致不平衡
使用方法:①先讓右孩子的左孩子,繞樹根右孩子進行一次右旋②樹根的新的右孩子繞樹根左旋一次
(4)左右旋
使用條件:根結點的左孩子的右子樹過長導致不平衡
使用方法:①先讓左孩子的右孩子,繞樹根左孩子進行一次左旋②樹根的新的左孩子繞樹根右旋一次

6.AVL樹
       平衡因子:右子樹的高度減去左子樹的高度。

平衡因子<-1 平衡因子>1
孩子的平衡因子<=-1 右旋 右左旋
孩子的平衡因子>=1 左右旋 左旋

7.紅黑樹
紅黑樹的特性:
       (1)每個節點或者是黑色,或者是紅色。
       (2)根節點是黑色。
       (3)每個葉子節點(Null)是黑色。 [注意:這裡葉子節點,是指為空(NULL)的葉子節點!]即每個空結點為黑色,也即預設結點為黑色
       (4)如果一個節點是紅色的,則它的子節點必須是黑色的。
       (5)從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點。
紅黑樹示意圖:

  • 紅黑樹中元素插入
  • 紅黑樹中元素刪除
           關於紅黑樹的元素插入和刪除,這兩部分將在教材學習問題中詳細說明。

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

  • 問題1:關於教材242頁中所說的“據此我們可以論證具有n個節點紅黑樹的最大高度約為2*logn”,這句話不能理解,從哪裡論證而來的結果?

  • 問題1解決方案:自己想試著推一下,推的話我的想法是必須要滿足紅黑樹的特徵,但從這下手試了一會沒有任何結果,於是就從網上查詢資料,給出的辦法是用歸納總結法去證明這一結論。
    "一棵含有n個節點的紅黑樹的最大高度約為2*logn"等價於“高度為h的紅黑樹,它的包含的節點個數至少為2^(h/2)個”。
    從某個節點x出發(不包括該節點)到達一個葉節點的任意一條路徑上,黑色節點的個數稱為該節點的黑高度(x's black height),記為bh(x)。關於bh(x)有兩點需要說明:
           第1點:根據紅黑樹的特性——從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點可知,從節點x出發到達的所有的葉節點具有相同數目的黑節點。這也就意味著,bh(x)的值是唯一的!
           第2點:根據紅黑色的特性——如果一個節點是紅色的,則它的子節點必須是黑色的可知,從節點x出發達到葉節點"所經歷的黑節點數目">= "所經歷的紅節點的數目"。假設x是根節點,則可以得出結論"bh(x) >= h/2"。進而,我們只需證明 "高度為h的紅黑樹,它的包含的內節點個數至少為 2^bh(x)個"即可。
    到這裡,我們將需要證明的定理已經由"一棵含有n個節點的紅黑樹的高度至多為2log(n)" 轉變成只需要證明"高度為h的紅黑樹,它的包含的內節點個數至少為 2bh(x)個"。

下面通過"數學歸納法"開始論證高度為h的紅黑樹,它的包含的節點個數至少為 2^bh(x)個"。

(01) 當樹的高度h=0時,內節點個數是1,bh(x) 為1,2^bh(x)也為 1。顯然,原命題成立。

(02) 當h>0,且樹的高度為 h-1 時,它包含的節點個數至少為 2^(bh(x)-1)。這個是根據(01)推斷出來的!
       下面,由樹的高度為 h-1 的已知條件推出“樹的高度為 h 時,它所包含的節點樹為 2bh(x)”。
       當樹的高度為 h 時,
       對於節點x(x為根節點),其黑高度為bh(x)。
       對於節點x的左右子樹,它們黑高度為 bh(x) 或者 bh(x)+1。
       根據(02)的已知條件,我們已知 "x的左右子樹,即高度為 h-1 的節點,它包含的節點至少為 2^(bh(x)-1) 個";
       所以,節點x所包含的節點至少為 ( 2^(bh(x)-1) ) + ( 2^(bh(x)-1) ) + 1 = 2^bh(x)。即節點x所包含的節點至少為 2bh(x)。
       因此,原命題成立。
       由(01)、(02)得出,"高度為h的紅黑樹,它的包含的內節點個數至少為 2^bh(x)個"。
       因此,“一棵含有n個節點的紅黑樹的高度至多為2log(n)”。

  • 問題2:關於紅黑樹的元素插入、刪除

  • 問題2解決方案:

插入

首先,我們插入的結點設定顏色為red,如果其父節點也為black,那麼沒有違背紅黑樹任何條件,插入即可,但如果其父節點為red,那麼需要調整,因為紅色結點的孩子顏色不能為紅。
插入一個結點時應該關注其叔叔結點?
因為在父結點為紅色的情況下,我們調整時必定要改動其父結點所構成的樹顏色,而這裡發生改變,那麼一定導致祖父結點處的左右子樹不能再滿足“從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點”,那麼我們需要對叔叔結點也進行改動,才能保持紅黑樹特性。
下面來看需要調整的三種情況(父結點為red,此時祖父結點必為黑色,以父結點為祖父左孩子為例):
(1)叔叔結點為紅色

此時我們需要把叔叔結點和父親結點都設為黑色,這樣不改變祖父結點下的“從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點”特性;
但還需將祖父結點設為紅色(因為在從根結點往下的到祖父往下的路徑上多了一個黑結點),並將當前結點設為祖父結點,然後往上迭代進行;
(2)叔叔結點為黑色,且插入結點為父結點右孩子

此時需將插入結點繞其父結點向左旋轉,這步不改變樹的各個路徑上的黑結點數目,但仍不滿足紅色結點的孩子必為黑色,此時轉換為(3)中情況;
為什麼要進行這一步轉化?
因為紅黑樹是一種平衡二叉樹,而我們要實現平衡,就要通過旋轉。這種情況是因左子樹的右孩子過長導致,所以我們使用的是左右旋,因此這裡先進行一次左旋。
(3)叔叔結點為黑色,且插入結點為父結點左孩子

現在,我們先繞祖父結點進行一次右旋,因為祖父結點為黑色,此時右子樹上要多一個黑色結點,且此時仍不滿足紅色結點孩子都為黑色;
我們這時需把當前結點設為黑色,另再將其左右兩個孩子結點設為紅色。

刪除

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

  • 問題1:課本程式碼BinarySearchTreeList類出現錯誤,錯誤型別為both methods have same erasure, yet neither overrides the other

  • 問題1解決方案:沒見過這個錯誤,去網上查詢資料,它的解釋是泛型型別擦除與過載和覆蓋問題,

    先看一段程式碼

public class Father {
    void test(Object o){}
}
class Son<T> extends Father{
     void test(T o){}//編譯錯誤!
}

這段程式碼會報一個編譯錯誤,both methods have same erasure, yet neither overrides the other。
這個錯誤的意思是,兩個方法在型別擦除後,具有相同的原生型別引數列表,但是也不能覆蓋另一個方法。
泛型型別在編譯後,會做型別擦除,只剩下原生型別。如引數列表中的T型別會編譯成Object,但是會有一個Signature。
儘管兩個test方法具有相同的位元組碼,但是型別引數資訊用 一個新的簽名(signature) 屬性記錄在類模式中。JVM 在裝載類時記錄這個簽名信息,並在執行時通過反射使它可用。
這就導致了這個方法既不能作為覆蓋父類test方法的方法,也不能作為test方法的過載。

       看過之後沒感覺,不太懂它在說什麼?又看了好幾遍,才終於有些明白,我的那個錯誤也是將我的方法中引數型別改為Object型別,但那樣還是會報錯,因為它的型別擦除後,父子類兩個重名方法並不能覆蓋。知道這之後,又檢視自己的程式碼,終於找到了是在我的OrderedListADT類中出了問題,即OrderedListADT類在繼承ListADT類時沒有寫出泛型,改過之後解決問題。

程式碼託管

       上週程式碼行數為12499行,現在為13751行,本週共1252行。

上週考試錯題總結

上週考試沒有錯題

結對及互評

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

    • 結對學習內容:第十一章——二叉查詢樹。

其他(感悟、思考等)

感悟

  • 本週的紅黑樹學習起來比較困難,書上的很多東西只是說出結論,並不解釋原因,這就增大了閱讀的難度,同時有些東西后面用到才會知道它前面所講得一些東西。

學習進度條

程式碼行數(新增/累積) 部落格量(新增/累積) 學習時間(新增/累積) 重要成長
目標 5000行 30篇 400小時
第一週 0/0 1/1 15/15
第二週 572/572 1/2 16/31
第三週 612/1184 1/3 13/44
第四周 1468/2652 2/5 13/57
第五週 1077/3729 1/6 14/71 初步理解各個排序演算法
第六週 1087/4816 1/7 17/88 認識樹結構
第七週 1252/6068 1/8 19/107 平衡二叉樹、AVL樹、紅黑樹

參考資料