1. 程式人生 > >二叉查詢樹--刪除結點——參考學習

二叉查詢樹--刪除結點——參考學習

刪除結點

刪除結點存在3種情況,分別為:

1、沒有左右子結點,可以直接刪除

刪除時需要判斷自己和父結點的關係,在左側還是右側

       如果父結點的左結點是自己,就清左側,否則清除右側  

//這裡忽略了父節點不存在的情況,最後會巧妙的處理這種情況
if(node.parent.left == node){
    node.parent.left = null;
} else {
    node.parent.right = null;
}   

2、存在左結點或者右結點,刪除後需要對子結點移動 

刪除結點,需要斷開兩個關係,然後建立父結點和子結點的關係

//先找到子節點,不需要管他是左是右
BSTNode<T> child = null;
if(node.left != null){
    child = node.left;
} else {
    child = node.right;
}
//這裡忽略了父節點不存在的情況,最後會巧妙的處理這種情況
//將父節點和子節點建立關係
if(node.parent.left == node){
    node.parent.left = child;
} else {
    node.parent.right = child;
}
child.parent = node.parent;

3、同時存在左右結點,不能簡單刪除,但是可以通過和後繼結點交換後轉為前兩種情況

當二叉查詢樹以中序遍歷時,遍歷的結果是一個從小到大排列的順序。

      當某個結點存在右結點時,後繼結點就是右結點中的最小值,由於左側結點總比右側結點和父結點小,所以後繼結點一定沒有左結點。從這一特點可以看出,後繼結點有可能存在右結點,也有可能沒有任何結點。由於後繼結點最多隻有一個子結點,因此刪除後繼結點時,就變成了3中情況中的前兩種。

轉移結點值的程式碼很容易:

//獲取當前節點的後繼結點
Node<T> successor = successor(node);
//轉移值
node.key = successor.key;
//後續變成刪除 successor,就變成了前兩種情況
//在圖示例子中,就是第一種沒有子節點的情況
node = successor;

實際上在三種情況中,還有一個特例就是刪除根結點

完整的刪除結點的程式碼

public void delete(T key){
		//獲取要刪除的結點
		BSTNode<T> node=search(mRoot, key);
		//如果存在就刪除
		if (node!=null) {
			delete(node);
		}
	}
	private BSTNode<T> delete(BSTNode<T> node) {
		//第3種情況,如果同時存在左右子結點
		if (node.left!=null && node.right!=null) {
			//獲取後繼結點
			BSTNode<T> successor=successor(node);
			//轉移後繼結點值到當前結點
			node.key=successor.key;
			//把要刪除的當前結點設定為後繼結點
			node=successor;
		}
		/**
		 * 經過前一步處理,下面只有兩種情況,只能是一個結點或者沒有結點
		 * 不管是否有子結點,都獲取子結點
		 */
		BSTNode<T> child;
		if (node.left!=null) {
			child=node.left;
		}else{
			child=node.right;
		}
		/**
		 * 如果child!=null,就說明有一個結點的情況
		 * 將父結點與子結點關聯上
		 */
		if (child!=null) {
			child.parent=node.parent;
		}
		/**
		 * 如果當前結點沒有父結點(後繼情況到這兒時一定有父結點)
		 * 說明要刪除的就是根結點
		 */
		if (node.parent==null) {
			/**
			 * 根結點設定為子結點
			 * 按照前面的邏輯,根只有一個或者沒有結點,所以直接賦child
			 */
			mRoot=child;
		}else if (node==node.parent.left) {
			/**
			 * 存在父結點,並且當前結點是左結點
			 * 將父結點的左結點設定為child
			 */
			node.parent.left=child;
		}else {
			/**
			 * 存在父結點,並且當前結點是右結點
			 * 將父結點的右結點設定為child
			 */
			node.parent.right=child;
		}
		//返回被刪除的結點
		return node;				
	}

總結刪除結點的思路

1、如果該結點同時存在左右子結點

        獲取後繼結點;

        轉移後繼結點值到當前結點node;

        把要刪除的當前結點設定為後繼結點successor。

2、經過步驟1的處理,下面兩種情況,只能是一個結點或者沒有結點。

    不管有沒有結點,都獲取子結點child

    if child!=null,就說明有一個結點的情況,此時將父結點與子結點關聯上

    if 當前結點沒有父結點(後繼情況到這一定有父結點),說明要刪除的就是根結點,

        根結點設定為child

    else if 當前結點是父結點的左結點

        則將父結點的左結點設定為child

    else 當前結點是父結點的右結點

        則將父結點的右結點設定為child

3、返回被刪除的結點node