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

2018-2019-20172329 《Java軟體結構與資料結構》第七週學習總結

2018-2019-20172329 《Java軟體結構與資料結構》第七週學習總結

教材學習內容總結

《Java軟體結構與資料結構》第十一章-二叉查詢樹

一、概述

  • 1、什麼是二叉查詢樹:二叉查詢樹是一種帶有附加屬性的二叉樹,即對樹中的每個結點,其左孩子都要小於其父結點,而父結點又小於或等於其右孩子。

  • 2、二叉查詢樹的定義是二叉樹定義的擴充套件。

  • 3、操作:

操作 描述
addElement 往樹中新增一個元素
removeElement 從書中刪除一個元素素
removeAllOccurrences 從樹中刪除所指定元素的任何存在
removeMin 刪除樹中的最小元素
removeMax 刪除樹中的最大元素
findMin 返回一個指向樹中最小元素的引用
findMax 返回一個指向樹中最大元素的引用

二、用連結串列實現二叉查詢樹

  • 1、addElement操作
    • 過程:如果這個樹為空,則新元素就將成為根結點。如果樹非空,這個新元素會與樹根元素進行對比。

      • (1)如果它小於根結點中儲存的那個元素且根的左孩子為null,則這個新元素就將成為根的左孩子。

      • (2)如果這個新元素小於根結點中儲存的那個元素且根的左孩子不是null,則會遍歷根的左孩子,並再次進行比較操作。

      • (3)如果這個新元素大於或等於樹根儲存的那個元素且根的右孩子為null,則這個新元素會成為根的右孩子,並再次進行比較操作。
    • 圖解:

  • 2、removeElement操作

    • 過程:

      • (1)如果被刪除結點沒有孩子,則replacement返回null。

      • (2)如果被刪除結點只有一個結點,則replacement返回這個孩子。

      • (3)如果被刪除結點有兩個孩子,則replacement會返回中序後繼者(因為相等元素會放到右邊)

      • 注:從二叉查詢樹中刪除一個元素時,必須推選出另一個結點來代替要被刪除的那個結點。
    • 圖解:

  • 3、 removeMin操作
    • 過程:
      • (1)如果樹根沒有左孩子,則樹根就是最小元素,而樹根的右孩子會變成新的根結點。
      • (2)如果樹的最左邊結點是一片葉子,則這片葉子就是最小元素,這時只需設定其父結點的左孩子引用為null即可。
      • (3)如果樹的最左側結點是一個內部結點,則需要設定其父結點的左孩子引用指向這個將刪除結點的右孩子。
      • 注:二叉查詢樹的最右側結點會存放最大元素,而其最左側結點會存放最小元素。
    • 圖解:

三、用有序列表實現二叉查詢樹

  • 1、BinarySearchTreeList實現的分析
操作 LinkedList BinarySearchTreeList
removeFirst O(1) O(log n)
removeLast O(n) O(log n)
remove O(n) O(log n)*
first O(1) O(log n)
last O(n) O(log n)
contains O(n) O(log n)
isEmpty O(1) O(1)
size O(1) O(1)
add O(n) O(log n)*

注:*add操作和remove操作都可能導致樹變的不平衡

四、平衡二叉樹

  • 1、下圖顯示了得到的二叉樹,這個結果二叉樹看作是一顆蛻化樹,看起來更像是一個連結串列,而事實上,它的效率比連結串列還低。

  • 2、如果沒有平衡假設,最壞情況下addElement操作時間複雜性是O(n)而不是O(log n),因為存在這種可能,即樹根是書中的最小元素,而將被插入的元素可能是樹中的最大元素。
  • 3、如果二叉查詢樹不平衡,其效率可能比線性結構的還要低。
  • 4、右旋
    • 用途:因為樹根左孩子的左子樹中較長的路徑而導致的不平衡。
    • 步驟:
      • (1)使樹根的左孩子元素成為新的根元素。
      • (2)使原根元素成為這個新樹根的右孩子元素。
      • (3)使原樹根的左孩子的右孩子,成為原樹根的新的左孩子。
    • 圖解:
  • 5、左旋
    • 用途:由樹根右孩子的右子樹中較長的路徑而導致的不平衡。
    • 步驟:
      • (1)使樹根的右孩子元素成為新的根元素。
      • (2)使原根元素成為這個新樹根的左孩子元素。
      • (3)使原樹根右孩子結點的左孩子,成為原樹根的新的右孩子。
    • 圖解:
  • 6、左右旋
    • 用途:對於由於樹根左孩子的右子樹中較長的路徑而導致的不平衡,我們必須先讓樹根左孩子的右孩子繞著樹根的左孩子進行一次左旋,然後再讓所得的樹根左孩子繞著樹根進行一次右旋。
    • 圖解:
  • 7、右左旋
    • 用途:對於由樹根右孩子的左子樹中較長路徑而導致的不平衡,我們必須先讓樹根右孩子的左孩子,繞著樹根右孩子進行一次右旋,然後再讓所得的樹根右孩子繞著樹根進行一次左旋。
    • 圖解:
  • 8、綜上所述,平衡化樹的一般性辦法,其中自樹根而下的路徑最大長度必須不超過log2 n,而且自樹根而下的路徑最小長度必須不小於(log2 n)-1。
    五、實現二叉查詢樹:AVL樹

  • 1、什麼是AVL樹
    • 它是上述樹的一種變體。對於樹中的每個結點,我們都會跟蹤起左右子樹的高度。對於樹中任何結點,如果其平衡因子(平衡因子指的是左右子樹的高度差,即右子樹的高度減去左子樹的高度)大於1或小於-1,則以該結點為樹根的子樹需要重新平衡。
  • 2、AVL樹的操作(右旋,左旋,左右旋,右左旋)
    • 右旋:
      • 用在:平衡因子<-1且孩子的平衡因子<=-1
    • 左旋:
      • 用在:平衡因子>1且孩子的平衡因子>=1
    • 左右旋:
      • 用在:平衡因子<-1且孩子的平衡因子>=1
    • 右左旋:
      • 用在:平衡因子>1且孩子的平衡因子<=-1
  • 刪除結點的例項圖:

  • AVL樹的性質:
    (1)左子樹和右子樹的高度之差的絕對值不超過1
    (2)樹中的每個左子樹和右子樹都是AVL樹
    (3)每個節點都有一個平衡因子(balance factor--bf),任一節點的平衡因子是-1,0,1之一
    六、實現二叉查詢樹:紅黑樹
  • 1、什麼是紅黑樹:紅黑樹是一種平衡二叉查詢樹,其中的每個結點儲存一種顏色(黑色或者紅色,通常用一個布林值來實現,值false等價於紅色)
  • 2、規則:
    • (1)根結點為黑色。
    • (2)紅色結點所有孩子都為黑色。
    • (3)從樹根到樹葉的每條路徑都包含同樣數目的黑色結點。

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

  • 問題1:在書中225頁中有一個類叫做超類,為什麼叫做超類?
  • 問題1解決方案:
  • 從網上查詢了以後得知:
    是父類。
    超類(SuperClass) :用java術語來講,被繼承的類稱為超類(SuperClass),也有叫做父類,繼承的類稱為子類。
    比如:
    public class A{//定義類A
    }
    public class B extends A{//定義類B,繼承類A
    }
    則,類A就是超類或父類,類B叫子類

  • 問題2:涉及紅黑樹的問題,對於我們大家都是一個難點,所以在教材問題中將紅黑樹具體來寫一下:
  • 問題2解決方案:
    (1)首先,我們需要了解到紅黑樹代表的是什麼:紅黑樹是一種平衡二叉查詢樹,其中的每個結點儲存一種顏色(紅色或黑色,通常用一個布林值來實現,值false等價於紅色)。控制結點顏色的規則如下:

  • 根結點為黑色。

  • 紅色結點的所有孩子都為黑色。

  • 從樹根到樹葉的每條路徑都包含同樣數目的黑色結點。

(2)我們瞭解到在某種程度上,紅黑樹中的平衡限制沒有AVL樹那樣的嚴格。但是,它們的序仍舊是log n。

(3)我們分析一下紅黑樹的時間複雜度:

  • 紅黑樹的時間複雜度為: O(lgn)
  • 下面通過“數學歸納法”對紅黑樹的時間複雜度進行證明。

  • 定理:一棵含有n個節點的紅黑樹的高度至多為2log(n+1).

  • 證明:
    "一棵含有n個節點的紅黑樹的高度至多為2log(n+1)" 的逆否命題是 "高度為h的紅黑樹,它的包含的內節點個數至少為 2h/2-1個"。

    我們只需要證明逆否命題,即可證明原命題為真;即只需證明 "高度為h的紅黑樹,它的包含的內節點個數至少為 2h/2-1個"。

    從某個節點x出發(不包括該節點)到達一個葉節點的任意一條路徑上,黑色節點的個數稱為該節點的黑高度(x's black height),記為bh(x)。關於bh(x)有兩點需要說明:
  • 第1點:根據紅黑樹的"特性(5) ,即從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點"可知,從節點x出發到達的所有的葉節點具有相同數目的黑節點。這也就意味著,bh(x)的值是唯一的!
  • 第2點:根據紅黑色的"特性(4),即如果一個節點是紅色的,則它的子節點必須是黑色的"可知,從節點x出發達到葉節點"所經歷的黑節點數目">= "所經歷的紅節點的數目"。假設x是根節點,則可以得出結論"bh(x) >= h/2"。進而,我們只需證明 "高度為h的紅黑樹,它的包含的黑節點個數至少為 2bh(x)-1個"即可。

    到這裡,我們將需要證明的定理已經由
    "一棵含有n個節點的紅黑樹的高度至多為2log(n+1)"
    轉變成只需要證明
    "高度為h的紅黑樹,它的包含的內節點個數至少為 2bh(x)-1個"

(4)紅黑樹中元素插入:
將一個節點插入到紅黑樹中,需要執行哪些步驟呢?首先,將紅黑樹當作一顆二叉查詢樹,將節點插入;然後,將節點著色為紅色;最後,通過旋轉和重新著色等方法來修正該樹,使之重新成為一顆紅黑樹。詳細描述如下:

1、第一步: 將紅黑樹當作一顆二叉查詢樹,將節點插入。
紅黑樹本身就是一顆二叉查詢樹,將節點插入後,該樹仍然是一顆二叉查詢樹。也就意味著,樹的鍵值仍然是有序的。此外,無論是左旋還是右旋,若旋轉之前這棵樹是二叉查詢樹,旋轉之後它一定還是二叉查詢樹。這也就意味著,任何的旋轉和重新著色操作,都不會改變它仍然是一顆二叉查詢樹的事實。
好吧?那接下來,我們就來想方設法的旋轉以及重新著色,使這顆樹重新成為紅黑樹!

2、第二步:將插入的節點著色為"紅色"。
為什麼著色成紅色,而不是黑色呢?為什麼呢?在回答之前,我們需要重新溫習一下紅黑樹的特性:

(1) 每個節點或者是黑色,或者是紅色。

(2) 根節點是黑色。

(3) 每個葉子節點是黑色。 [注意:這裡葉子節點,是指為空的葉子節點!]

(4) 如果一個節點是紅色的,則它的子節點必須是黑色的。

(5) 從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點。
將插入的節點著色為紅色,不會違背"特性(5)"!少違背一條特性,就意味著我們需要處理的情況越少。接下來,就要努力的讓這棵樹滿足其它性質即可;滿足了的話,它就又是一顆紅黑樹了。o(∩∩)o...哈哈

3、第三步: 通過一系列的旋轉或著色等操作,使之重新成為一顆紅黑樹。
第二步中,將插入節點著色為"紅色"之後,不會違背"特性(5)"。那它到底會違背哪些特性呢?
對於"性質(1)",顯然不會違背了。因為我們已經將它塗成紅色了。
對於"性質(2)",顯然也不會違背。在第一步中,我們是將紅黑樹當作二叉查詢樹,然後執行的插入操作。而根據二叉查詢數的特點,插入操作不會改變根節點。所以,根節點仍然是黑色。
對於"性質(3)",顯然不會違背了。這裡的葉子節點是指的空葉子節點,插入非空節點並不會對它們造成影響。
對於"性質(4)",是有可能違背的!
那接下來,想辦法使之"滿足特性(4)",就可以將樹重新構造成紅黑樹了。

(5)紅黑樹中元素刪除:
-(1)重新平衡化(及重新著色):刪除元素之後進行這一操作,是一種迭代過程,從刪除點開始,一直上溯到樹根。因此,如前所述,實現紅黑樹時最好在各個結點中包含一個父結點引用。這一過程的終止條件是(current==root)或(current.color==red),其中current是我們正在處理的這個結點。

  • (2)在刪除中而言,焦點要放在當前結點的兄弟的顏色上。
    • 如果兄弟的顏色是red,則在做其他事之前必須完成如此處理步驟:
      • (1)設定兄弟的顏色為black;
      • (2)設定current的父親的顏色為red;
      • (3)讓兄弟繞著current的父親向右旋轉;
      • (4)設定兄弟等於current的父親的左孩子;
    • 如果兄弟的兩個孩子都是black或null,則需要做到:
      • (1)設定兄弟的顏色為red;
      • (2)設定current等於current的父親;
    • 如果兄弟的兩個孩子不全為black,則將檢視兄弟的左孩子是否是black。如果是,則在繼續之前必須完成如下步驟:
      • (1)設定兄弟的右孩子的顏色為black;
      • (2)設定兄弟的顏色為red;
      • (3)讓兄弟的右孩子繞著兄弟本身向右旋轉。
      • (4)設定兄弟等於current的父親的左孩子;
    • 最後是兄弟的兩個孩子都不為black這一情況,這時必須:
      • (1)設定兄弟的顏色為current的父親的顏色;
      • (2)設定current的父親的顏色為black;
      • (3)設定兄弟的左孩子的顏色為black;
      • (4)讓兄弟繞著current的父親向右旋轉;
      • (5)設定current等於樹根。
    • 該迴圈終止之後,我們要刪除該結點,並設定其父親的孩子引用為null。

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

  • 問題1:在補全程式碼的時候,發現有一個欄位自己好像沒有見過instanceof
  • 問題1解決方案:
    (1)自己也查了查這個到底是什麼?用法如下:
  • 1、java 中的instanceof 是一個二元操作符(運算子)運算子,由於是字母組成,所以是Java的保留關鍵字,但是和>=,<=,==屬同一類,它的作用是用來判斷,instanceof 左邊物件是否為instanceof 右邊類的例項,返回一個boolean型別值。還可以用來判斷子父類的所屬關係。
  • 2、用法:
    boolean result = object instanceof class
    引數:
    Result:布林型別。
    Object:必選項。任意物件表示式。
    Class:必選項。任意已定義的物件類。
    說明:
    如果 object 是 class 的一個例項,則 instanceof 運算子返回 true。如果 object 不是指定類的一個例項,或者 object 是 null,則返回 false。

程式碼連結


上週考試錯題總結

測試還沒有結束

結對及互評

  • 本週結對學習情況
  • 部落格中值得學習的或問題:
    • 內容詳略得當;
    • 程式碼除錯環節比較詳細;
  • 基於評分標準,我給本部落格打分:5分。得分情況如下:
  1. 正確使用Markdown語法(加1分):
  2. 模板中的要素齊全(加1分)
  3. 教材學習中的問題和解決過程, 一個問題加1分
  4. 程式碼除錯中的問題和解決過程, 一個問題加1分

  • 部落格中值得學習的或問題:
    • 內容詳略得當;
    • 程式碼除錯環節比較詳細;
  • 基於評分標準,我給本部落格打分:9分。得分情況如下:
  1. 正確使用Markdown語法(加1分):
  2. 模板中的要素齊全(加1分)
  3. 教材學習中的問題和解決過程, 一個問題加1分
  4. 程式碼除錯中的問題和解決過程, 一個問題加1分

感悟

這一週很顯然的感覺到自己時間很不夠用,可能自己對於時間的分配還是有些不合理,總是做了些浪費時間的事情,也發現自己現在的時間觀念沒有過去那樣很強了,希望自己可以經常反省,自己為什麼會做事越來越慢。

學習進度條

程式碼行數(新增/累積) 部落格量(新增/累積) 學習時間(新增/累積)
目標 5000行 30篇 400小時
第一週 0/0 1/1 6/6
第二週 1313/1313 1/2 20/26
第三週 901/2214 1/3 20/46
第四周 3635/5849 2/4 20/66
第五週 1525/7374 1/5 20/86
第六週 1542/8869 2/5 25/111
第七週 1391/10260 1/6 20/131

參考資料

藍墨雲班課
Java程式設計
紅黑樹之原理和演算法實現
java中instanceof的用法和實戰
AVL樹(平衡二叉樹)
AVL樹演算法思想和程式碼實現
AVL樹的旋轉圖解和簡單實現