1. 程式人生 > >20172301 《程式設計與資料結構》第七週學習總結

20172301 《程式設計與資料結構》第七週學習總結

20172301 《程式設計與資料結構》第七週學習總結

教材學習內容總結

  • 二叉查詢樹是一種含有附加屬性的二叉樹,其左孩子小於父結點,父結點小於或者等於右孩子。

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

  • addElement操作:根據給定元素的值,在樹中的恰當位置新增該元素。
    • 判斷元素是不是Comparable,不是則丟擲異常。
    • 樹為空:新元素成為根結點。
    • 樹非空:新元素與根元素進行比較
      • 小於:如果根的左孩子為空,成為根的左孩子;左孩子不空,遍歷新增。
      • 大於:如果根的右孩子為空,成為根的右孩子;右孩子不空,遍歷新增。
  • removeElement操作:從二叉查詢樹中刪除給定的Comparable元素;找不到則丟擲異常。
    • 選擇替換結點的三種情況:
      (1)被刪除結點沒有孩子,replacement返回null;
      (2)被刪除結點有一個孩子,replacement返回這個孩子 ;
      (3)被刪除結點有兩個孩子,replacement返回中序後繼者;(處於根結點右子樹上)
  • removeAllOccurrences操作:從二叉查詢樹中刪除指定元素的所有存在。
    • 方法使用了LinkedBinaryTree類的contains方法。
  • removeMin操作:
    • 最小元素在二叉查詢樹的可能情況:
      (1)樹根沒有左孩子,樹根即為最小元素,樹根右孩子變成新的根結點;
      (2)樹的最左側結點為一片葉子,該葉子即為最小元素,設定其父結點的左孩子應用為null;
      (3)樹的最左側結點為內部結點,設定其父結點的左孩子引用指向最小元素的右孩子。

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

  • 問題1:關於書P228頁的中序後繼者的理解。
  • 問題1解決方案:
    • 所謂的中序後繼者意思是:中序遍歷二叉樹結點的後繼結點
    • 如何查詢中序後繼者?
      • 若右子樹不為空,則找到右子樹最左的葉子節點;
      • 若右子樹為空,且擁有右父親節點,則找到右父親節點;
      • 若右子樹為空,且擁有左父親節點,則找到最近的右祖先節點;
    • 而對於刪除結點有兩個孩子的情況時,不一定replacement返回中序後繼者。也可以返回中繼前驅者。 具體的需要看程式碼實現,而不需要侷限於書本。
    • 如何查詢中序前驅者?
      • 若左子樹不為空,則找到左子樹的最右的葉子節點;
      • 若左子樹為空,且擁有左父親節點,則找到左父親節點;
      • 若左子樹為空,且擁有右父親節點,則找到最近的左父祖先節點;
  • 問題2:
  • 問題2解決方案:XXXXXX
  • ...

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

  • 問題1:是否需要定義新的指標類AVLTreeNode,換句話說,AVL樹和二叉查詢樹以及連結串列實現的二叉樹之間的關係。
  • 問題1解決方案:
    • 首先,根據書上P240所述

      由於需要上溯樹,因此AVL樹通常最好實現為每個結點都包含一個指向其父結點的引用。

    • 這裡的上溯樹是因為,樹因為插入結點或者刪除結點而變得不平衡,所以每次在進行這兩個操作的時候,需要更新平衡因子,從插入或者刪除的那個結點開始,檢查到根結點。所以,我們的指標類很可能除了指向左右孩子的指標,還需要一個指向父結點的。
    • 其次,根據書上P239所述

      對於樹中的每個結點,我們都會跟蹤其左、右子樹的高度。

    • 由此,指標類會需要一個int型變數height,來得出結點的高度。
    • 在我實現了指標類AVLTreeNodeLinkedAVLTree的平衡方法後,我需要實現新增和刪除方法。但是,AVL樹和二叉查詢樹唯一不同的是新增和刪除中如果不平衡要進行旋轉。 所以,AVL樹是可以繼承二叉查詢樹的。
    • 這時,其實我陷入了一個思維誤區。我寫的指標類AVLTreeNode因此肯定也要繼承二叉樹指標類BinaryTreeNode。但是,其實根本不用這麼麻煩呀!
      直接在BinaryTreeNode構建新的構造方法不就可以了!
    public BinaryTreeNode(T obj, LinkedBinaryTree<T> left, LinkedBinaryTree<T> right,int height)`
    • 存在的問題:
      雖然準確理解了AVL樹中旋轉平衡的操作,但是並沒有整體理解程式碼與程式碼之間的關係。花費大量的時間做了無用功,同時讓自己陷入了錯誤的迴圈。
      如果,我直接發現AVL樹是二叉查詢樹的子類,那我也不會構建新的指標類。
      所以,解決程式碼問題,首先需要巨集觀的觀察,確定好整體的架構,這便是UML類圖的重要性。不然,儘管你細節處理的再完美,方向錯了,便是越走越遠。

      先設計,考慮所有的情況,再去實現。

  • 問題2:連結串列旋轉方法的順序問題。
  • 問題2解決方案:這裡以右旋為例。
    • 根據書P238 給出右旋的操作

      • 使樹根的左孩子元素成為新的根元素。
      • 使原根元素成為這個新樹根的右孩子元素。
      • 使原樹根的左孩子的右孩子,成為原樹根的新的左孩子。
    • 所以我們實現右旋方法就可以使用一下操作,其中node是原樹根,node1是新樹根。
    node1 = node.left;
    node1.right = node;
    node.left = node1.right;
    • 然後,新增上更新高度的操作。就可以返回新的根元素。
    node.height = Math.max(height(node.left),height(node.right));
    node1.height = Math.max(height(node1.left),height(node1.right));
    return node1;
    • 執行,首先給我丟擲的是StackOverflowError錯誤。

    • 當應用程式遞迴太深而發生堆疊溢位時,丟擲該錯誤。也就是說,方法裡出現了死遞迴。這個問題,我在上週侯澤洋同學的部落格中也看見過。
  • ...

程式碼託管

上週考試錯題總結

上週無錯題,優秀!

結對及互評

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

其他

學習進度條

程式碼行數(新增/累積) 部落格量(新增/累積) 學習時間(新增/累積) 重要成長
目標 5000行 30篇 400小時
第一週 0/0 1/1 10/10
第二週 610/610 1/2 20/30
第三週 593/1230 1/3 18/48
第四周 2011/3241 2/5 30/78
第五週 956/4197 1/6 22/100
第六週 2294/6491 2/8 20/120
第七週 914/7405 1/9 20/140

參考資料