1. 程式人生 > >20172306 2018-2019-2 《Java程式設計與資料結構》第七週學習總結

20172306 2018-2019-2 《Java程式設計與資料結構》第七週學習總結

20172306 2018-2019-2 《Java程式設計與資料結構》第七週學習總結

教材學習內容總結

  • 概述
    • 二叉查詢樹是一種含有附加屬性的二叉樹,即其左孩子小於父結點,而父結點又小於或等於右孩子。
    • 二叉查詢樹的定義是二叉樹定義的擴充套件。
    • 二叉查詢樹的各種操作:addElement ,removeElement ,removeAllOccurrences(從樹中刪除所指定元素的任何存在) ,removeMin ,removeMax ,findMin ,finMax
  • 用連結串列實現二叉查詢樹
    • 每個BinaryTreeNode物件要維護一個指向結點所儲存元素的引用,另外還要維護指向結點的每個孩子的引用。

    • addElement操作(假設:根結點為node)
      • (1)如果樹為空,則插入的element為根結點
      • (2)如果樹不為空,element<node,且node的左孩子為null,則null的空為element;如果node的左孩子不為null,則繼續遍歷根的左孩子,再進行操作。
      • (3)如果樹不為空,element>= node,且node的右孩子為null,則null的空為element;如果node的右孩子不為null,則繼續遍歷根的右孩子,再進行操作。
    • removeElement操作
      • 與前面的線性結構研究不同,這裡不能簡單地通過刪除指定結點的相關引用指標而刪除該結點。相反,這裡必須推選出另一個結點來代替要被刪除的那個結點。受保護方法replacement返回指向一個結點的引用,該結點將代替要刪除的結點 。
      • 替換的replacement的程式碼如下:
      private BinaryTreeNode<T> replacement(BinaryTreeNode<T> node) {
          BinaryTreeNode<T> result = null;
          if ((node.left == null) && (node.right == null)) {
              result = null;
          } else if ((node.left != null) && (node.right == null)) {
              result = node.left;
      
          } else if ((node.left == null) && (node.right != null)) {
              result = node.right;
          } else {
              BinaryTreeNode<T> current = node.right;// 初始化右側第一個結點
              BinaryTreeNode<T> parent = node;
      
              // 獲取右邊子樹的最左邊的結點
              while (current.left != null) {
                  parent = current;
                  current = current.left;
              }
      
              current.left = node.left;
      
              // 如果當前待查詢的結點
              if (node.right != current) {
                  parent.left = current.right;// 整體的樹結構移動就可以了
                  current.right = node.right;
              }
      
              result = current;
          }
          return result;
      }
    • 選擇替換結點的三種情況:
      • 如果被刪除結點沒有孩子,則replacement返回null
      • 如果被刪除結點只有一個孩子,則replacement返回這個孩子
      • 如果被刪除結點有兩個孩子,則replacement會返回中序後繼者(因為相等元素會放到右邊)
    • 對於這個刪除,有幾種情況:
      • (1)要刪除的結點無孩子結點;刪除方法:直接刪除該結點
      • (2)要刪除的結點只有左孩子結點;刪除方法:刪除該結點且使被刪除節點的雙親結點指向被刪除節點的左孩子結點;
      • (3)要刪除的結點只有右孩子結點;刪除方法:刪除該結點且使被刪除節點的雙親結點指向被刪除結點的右孩子結點
      • (4)要刪除的結點有左、右孩子結點;刪除方法:找中序後繼者,用它的值填補到被刪除節點中,再來處理該結點的刪除
    • removeAllOccurrences操作
      • 我覺得這個操作主要還是利用removeElement。和removeElement不同的是,我們會利用一個find的操作來判斷有沒有這個targetElement的存在,有的話,就繼續利用removeElement的相關方法進行刪除。沒有這個targetElement就丟擲異常。
      public void removeAllOccurrences(T targetElement) {
          removeElement(targetElement);//在我看來就是利用了之前的刪除元素的方法
      
          try {
              while (contains((T) targetElement))
                  removeElement(targetElement);
          }
      
          catch (Exception ElementNotFoundException) {
          }
      
      }
    • removeMin操作(二叉查詢樹的最右側結點會存放最大元素,最左側結點會存放最小元素)
      • 我認為removeMax和removeMin的原理是相同的,懂了removeMin就會removeMax了。
      • 最小元素的位置的3種情形:(假設:樹根是root)
        • (1)如果root沒有左孩子,則root為min,root的右孩子就會變成新的root
        • (2)如果樹的最左側結點是一片葉子,則這個葉子就是min,這時只需設定其父結點的左孩子引用為null
        • (3)如果樹的最左側結點是一個內部結點,則需要設定其父結點的左孩子引用指向這個將刪除結點的右孩子
  • 用有序列表實現二叉查詢樹
    • 樹的主要使用之一就是尾其他集合提供高效的實現
    • add和remove操作都會對樹的平衡有影響,而這一點同時對所使用的演算法的分析也有影響
  • 平衡二叉查詢樹
    • 如果二叉查詢樹不平衡其效率可能比線性結構的還要低
    • 右旋:a.根的左孩子為新的根元素;b.原根成為新根的右孩子;c.原根的左孩子的右孩子為原根的新的左孩子。(如果是因為樹根左孩子的左子樹中較長的路徑而導致的不平衡,右旋可以解決它)

    • 左旋:a.根的右孩子為新的根元素;b.原根成為新根的左孩子;c.原根的右孩子的左孩子為原根的新的右孩子。 (對於由樹根右孩子的右子樹中較長路徑而導致的不平衡,左旋可以解決它)

    • 右左旋:先將子樹中左旋變成右子樹比較長的,然後右旋。
    • 左右旋:先將子樹中右旋變成左子樹比較長的,然後左旋。
    • 瞭解了左旋和右旋,其實右左旋和左右旋都是在這兩個的基礎上進行的。

  • 這兩種二叉查詢樹:我們以上的這幾種操作都是在平衡二叉查詢樹的條件下進行的,而AVL樹和紅黑樹就是將不是平衡的樹變成平衡樹。
  • 樹只有兩個途徑能變得不平衡:插入結點和刪除結點。
    • 原因:我認為是根據之前的插入和刪除的操作,可以知道在插入和刪除的時候,會對樹的長度發生很大的變化,所以極可能導致不平衡。
  • 對於AVL樹和紅黑樹,都要從插入和刪除結點的位置需要上溯樹,所以通常最好實現為每個結點都包含一個指向其父結點的引用。

  • 實現二叉查詢樹:AVL樹
    • 平衡化樹的一般性方法:其中自樹根而下的路徑最大長度必須不超過logn,而且自樹根而下的路徑最小長度必須不小於logn-1。
    • 平衡因子:其左右子樹的高度差(即右子樹的高度減去左子樹的高度)——大於1或者小於-1,則以該結點為樹根的子樹需要重新平衡。
    • 我認為對於AVL的右旋、左旋、右左旋、左右旋的過程和之前的沒有區別,我們學習AVL樹的就是,它註明了平衡因子,而這個就是為了我們在看到一個樹時,判斷是利用哪種旋的方式的。
  • 實現二叉查詢樹:紅黑樹
    • 紅黑樹的樣子:
    • 其中白色代表紅色,黑色代表黑色。
    • 結點儲存一種顏色(紅色或黑色,通常用一個布林值來實現,false等價於紅色)
    • 規則
      • (1)根結點為黑;
      • (2)葉子的結點也為黑,因為每個葉子後面的都跟著一個null,null是黑色結點,所以是黑色結點。
      • (3)紅結點的所有孩子都為黑色;
      • (4)從樹根到樹葉的每條路徑都包含同樣數目的黑色結點
    • 由於紅色結點不能有紅色孩子,所以路徑中至多有一半結點是紅色結點、至少有一半結點是黑色結點
    • 插入
      • 一般設定要插入結點的顏色為Red。
      • 插入時分為三種情況:
        • 被插入為根結點,那麼就直接染色為黑色
        • 被插入結點的雙親結點為黑色,不用做,因為對於黑色沒有什麼影響。
        • 被插入結點的雙親結點為紅色,這就會有三種情況:
          • case1:當前結點的雙親結點A為紅色,且其祖父的另一個子結點為紅:
          • case2:當前結點的雙親結點A為紅色,且其叔叔結點為黑色,且其為其雙親結點的右孩子:
          • case3:當前結點的雙親結點是紅色,其叔叔結點為黑色,當前結點是其雙親結點的左孩子:
    • 刪除
      • 有三種情況:
        • one:要刪除結點沒有孩子結點:直接刪除
        • two:要刪除結點只有一個孩子結點:直接使用其孩子結點代替要刪除結點
        • three:要刪除結點有左右孩子結點:要考慮後繼結點的問題

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

  • 問題1:在書中說,LinkedBinarySearchTree類提供了兩個建構函式,這兩個函式都只是引用了其超類(LinkedBinaryTree類),但是好像很少聽到超類這個詞
  • 問題1解決方案:我上網查了下,就暴露了我以前書看的不好的問題了。原來超類就是父類啊。
public class A{//定義類A
}
public class B extends A{//定義類B,繼承類A
}
則,類A就是超類或父類,類B叫子類
  • 問題2:書中這句,如果被刪除結點有兩個孩子,則replacement會返回中序後繼者,中序後繼者這個詞我沒有理解?
  • 問題2解決方案:這個位置我回到第十章那個中序遍歷那看了一眼。用一個圖來表示我的收穫。

    而且我還得到了,其實中序後繼者,就是找這個要刪除的結點的右子樹中最小的那個值,這個值就是中序後繼者。

  • 問題3:書中的紅黑樹部分說,規則之一是從樹根到樹葉的每條路徑都包含同樣數目的黑色結點,但是書中有個圖

  • 問題3解決方案:這個是不對的,因為不符合規則中的每條路徑的黑色結點相同;且這個可以看出,對於紅黑樹,它的平衡性不是特別的嚴格。

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

  • 問題1:在對LinkedBinarySearchTree進行測試時,出現了這樣的問題。,它出現了地址。

  • 問題1解決方案:最開始我覺得肯定沒問題啊,後來出現這樣了。這個問題其實挺普遍的,是toString方法沒寫的緣故,我忘記寫了!!!它這個也是樹,所以我直接就將第十章的printTree的給拿過來了。

程式碼託管

上週考試錯題總結

  • 上週沒有錯題

結對及互評

點評模板:

  • 部落格中值得學習的或問題:
    • 內容總結的比較詳細

點評過的同學部落格和程式碼

  • 本週結對學習情況
    • 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
第七週 / 1/1 56/207

參考資料