20172324 2018-2019-1 《程式設計與資料結構》第七週學習總結
20172324 2018-2019-1 《程式設計與資料結構》第七週學習總結
教材學習內容總結
概述
二叉查詢樹是一種含有附加屬性的二叉樹,即其左孩子小於父節點,而父節點又小於等於其右孩子。
它是特殊的二叉樹:對於二叉樹,假設x為二叉樹中的任意一個結點,x節點包含關鍵字key,節點x的key值記為key[x]。如果y是x的左子樹中的一個結點,則key[y] <= key[x];如果y是x的右子樹的一個結點,則key[y] >= key[x]。那麼,這棵樹就是二叉查詢樹。如下圖所示:
在二叉查詢樹中: (01) 若任意節點的左子樹不空,則左子樹上所有結點 的值均小於它的根結點的值; (02) 任意節點的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值; (03) 任意節點的左、右子樹也分別為二叉查詢樹。
(04) 沒有鍵值相等的節點(no duplicate nodes)。
二叉查詢樹的介面類繼承自二叉樹的介面類BinaryTreeADT,在其基礎上補充了一些操作方法
操作 | 說明 |
---|---|
addElement | 往樹中新增一個元素 |
removeElement | 從樹中刪除一個元素 |
removeAllOccurrences | 從樹中刪除所指定元素的任何存在 |
removeMin | 刪除樹中最小元素 |
removeMax | 刪除樹中最大元素 |
findMin | 返回一個指向樹中的最小元素的引用 |
findMax | 返回一個指向樹中的最大元素的引用 |
連結串列實現二叉查詢樹
- 每個BinaryTreeNode物件要維護一個指向結點所儲存元素的引用,另外還要維護指向結點的每個孩子的引用。
- LinkedBinarySearchTree類提供了兩個建構函式
//建立空的LinkedBinarySearchTree public LinkedBinarySearchTree() { super(); } //建立一棵根結點為特定元素的LinkedBinarySearchTree public LinkedBinarySearchTree(T element) { super(element); if (!(element instanceof Comparable)) throw new NonComparableElementException("LinkedBinarySearchTree"); }
addElement操作:根據二叉查詢樹的性質,插入一個節點的時候,如果根節點為空,就此節點作為根節點,如果根節點不為空,就要先和根節點比較,如果比根節點的值小,就插入到根節點的左子樹中,如果比根節點的值大就插入到根節點的右子樹中,如此遞迴下去,找到插入的位置。
例圖(插入65,黃色線為比較的軌跡):
removeElement操作:
初始狀態:被刪除的節點只有左節點或者只有右節點,這種情況好辦,因為節點在一條鏈上,沒有分叉,就像處理連結串列一樣把這個節點摘掉就行了。讓它的父節點關聯它的子節點,它的子節點關聯它的父節點就完事。如果它沒有父節點,說明它是根節點,直接將其子節點作為根節點就行。
滿足這個情況的節點有 34, 70 兩個節點,這裡以 70 為例,如下圖所示:
刪除 70 的時候,需要斷兩個關係,然後建立父節點和子節點的關係,經過上述操作後,節點狀態如下圖所示:- 被刪除的節點沒有子節點,這種情況也很簡單,它是葉子節點,直接置空,將其父節點對應的子節點也置空,就完事。
在我們圖中,符合這個條件的有 20,32,40,75,100,隨便找個 20 來演示刪除該節點:
這種情況是最簡單的,我們只需要刪除該節點和父節點的關係即可。刪除的時候需要先判斷自己和父節點的關係是左側還是右側,如果父節點的左節點是自己,就清左側,否則就是右側。刪除後如下圖所示: 被刪除的節點有左右子節點。樹結構中的所有節點按順序拍好的話,它的前驅和它的後繼兩個節點剛好在它左右緊挨著它。當一個節點被刪除時,為了保證二叉樹的結構不被破壞,要讓它的前驅或者後繼節點來代替它的位置,然後將它的前驅或者後繼節點同樣做刪除操作。
滿足同時存在左右節點的節點有 50,30,80,35 這 4 個節點,30 看起來更復雜,我們以 30 為例。
當二叉查詢樹以中序遍歷時,遍歷的結果是一個從小到大排列的順序,如下圖所示:
當我們刪除 30 節點的時候,整個中序遍歷的結果中,從 32 開始都往前移動了一位。32 是 30 的後繼節點,就是比 30 大的節點中最小的節點。當某個節點存在右節點時,後繼結點就是右節點中的最小值,由於左側節點總比右側節點和父節點小,所以後繼節點一定沒有左節點。從這一個特點就能看出來,後繼結點有可能存在右節點,也有可能沒有任何節點。後繼結點還有一個特點,就是他比 30 的左節點大,比 30 所有的右節點都小,因此刪除 30 的時候,可以直接將後繼結點 32 的值(key)轉移到 30 節點上,然後刪除後繼結點 32。由於後繼結點最多隻有一個子節點,因此刪除後繼節點時,就變成了 3 種情況中的前兩種。圖示如下:
- removeAllOccurrences操作:可以看做呼叫了removeElement,當在樹中找不到指定元素是,則丟擲ElementNotFoundException異常,如果指定的元素不是Comparable,則removeAllOccurrences方法也會丟擲ClassCaseException異常。只要樹中還含有目標元素,就會再次呼叫removeElement方法。
- removeMin操作:最小元素在樹中位置的3種情形:
- 樹根沒有左孩子,樹根即為最小元素,樹根右孩子變成新的根結點;
- 樹的最左側結點為一片葉子,該葉子即為最小元素,設定其父結點的左孩子應用為null;
- 樹的最左側結點為內部結點,設定其父結點的左孩子引用指向最小元素的右孩子。
用有序列表實現二叉查詢樹
列表的一些常見操作:
操作 | 說明 | LinkedList | BinarySearchTreeList |
---|---|---|---|
removeFirst | 刪除列表的首元素 | O(1) | O(logn) |
removeLast | 刪除列表的末元素 | O(n) | O(logn) |
remove | 刪除列表中的一個特定元素 | O(n) | O(logn) |
first | 考察列表前端的那個元素 | O(1) | O(logn) |
last | 考察列表末端的那個元素 | O(n) | O(logn) |
contains | 判斷列表是否含有一個特定元素 | O(n) | O(logn) |
is Empty | 判定列表是否為空 | O(1) | O(1) |
size | 判定列表中的元素數目 | O(1) | O(1) |
有序列表特有的操作:
操作 | 說明 | LinkedList | BinarySearchTreeList |
---|---|---|---|
add | 向列表新增一個元素 | O(n) | O(logn) |
平衡二叉查詢樹
如果沒有平衡假設,它的效率比連結串列的還低,因為每個結點還附帶額外的開銷。
- AVL樹的旋轉規律:
LL型
平衡二叉樹某一節點的左孩子的左子樹上插入一個新的節點,使得該節點不再平衡。這時只需要把樹向右旋轉一次即可,如圖所示,原A的左孩子B變為父結點,A變為其右孩子,而原B的右子樹變為A的左子樹,注意旋轉之後Brh是A的左子樹
RR型
平衡二叉樹某一節點的右孩子的右子樹上插入一個新的節點,使得該節點不再平衡。這時只需要把樹向左旋轉一次即可,如圖所示,原A右孩子B變為父結點,A變為其左孩子,而原B的左子樹Blh將變為A的右子樹。
LR型
平衡二叉樹某一節點的左孩子的右子樹上插入一個新的節點,使得該節點不再平衡。這時需要旋轉兩次,僅一次的旋轉是不能夠使二叉樹再次平衡。如圖所示,在B節點按照RR型向左旋轉一次之後,二叉樹在A節點仍然不能保持平衡,這時還需要再向右旋轉一次。
- RL型
平衡二叉樹某一節點的右孩子的左子樹上插入一個新的節點,使得該節點不再平衡。同樣,這時需要旋轉兩次,旋轉方向剛好同LR型相反。
- 實現二叉查詢樹:AVL樹
- 平衡因子:右子樹的高度減去左子樹的高度。
比較 | 平衡因子<-1 | 平衡因子>1 |
---|---|---|
孩子的平衡因子<=-1 | 右旋 | 右左旋 |
孩子的平衡因子>=1 | 左右旋 | 左旋 |
- 實現二叉查詢樹:紅黑樹
紅黑樹的特性:
- 每個節點或者是黑色,或者是紅色。
- 根節點是黑色。
- 每個葉子節點(Null)是黑色。 [注意:這裡葉子節點,是指為空(NULL)的葉子節點!]即每個空結點為黑色,也即預設結點為黑色
- 如果一個節點是紅色的,則它的子節點必須是黑色的。
- 從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點。
(接下來的部分都讓我處於神遊狀態,先把王老師的講解粘下來吧)
紅黑樹-插入:
紅黑樹-刪除:
過於複雜 放在教材學習問題中
教材學習中的問題和解決過程
問題1:根據二叉查詢樹的定義,下面這個圖是否正確?
問題1解決方案:不正確,雖然它作為右子樹滿足大於等於它的父節點這一個要求,但它仍然是根結點左子樹。90大於56了,所以不滿足。可以放在99的左子樹
問題2:
- 問題2解決方案:
問題3:在課上提出來的一個問題,如下圖所示,該二叉樹不滿足紅黑二叉樹,一種方法是將10重新染色成黑色,再將7染色成紅色。現在問題來,能不能把10和18都染成黑色?
問題3解決方案:
程式碼除錯中的問題和解決過程
問題一:
問題一解決方案:
程式碼託管
上週考試錯題總結
無錯題
結對及互評
點評過的同學部落格和程式碼
- 本週結對學習情況
- 結對同學學號21
- 本週結對學習情況
內容詳略得當;
程式碼除錯環節比較詳細,出現得問題都差不多
其他(感悟、思考等,可選)
學習進度條
程式碼行數(新增/累積) | 部落格量(新增/累積) | 學習時間(新增/累積) | 重要成長 | |
---|---|---|---|---|
目標 | 5000行 | 30篇 | 400小時 | |
第一週 | 0 | 1/1 | 20/20 | |
第二週 | 300/500 | 1/2 | 18/38 | |
第三週 | 300/600 | 1/3 | 18/38 | |
第四周 | 400/1000 | 2/5 | 18/38 | |
第五週 | 300/1300 | 1/6 | 18/38 | |
第六週 | 300/1300 | 3/9 | 18/38 | |
第七週 | 300/1300 | 3/9 | 18/38 |