1. 程式人生 > >二叉樹中刪除一個節點

二叉樹中刪除一個節點

 二叉樹的刪除可以算是二叉樹最為複雜的操作,刪除的時候要考慮到很多種情況:
1.被刪除的節點是葉子節點
2.被刪除的節點只有左孩子節點
3.被刪除的節點只有右孩子節點
4.被刪除的有兩個孩子節點
 
所以在刪除的時候,這4種情況都必須考慮進去,並且這4中情況之下,還會有細的劃分,下面就細說怎麼刪除。
 
在二叉樹中想要刪除一個節點,首先需要找到這個節點,由於二叉樹在插入節點的時候會遵循一個原則,就是利用節點的值來判斷
節點將被插入的位置(或者給節點加一個key,用key來判斷)。從根節點開始往下走,比當前節點小(或等於)的會被插入到
當前節點的左邊,比當前節點大的會被插入到當前節點的右邊。所以,根據根據二叉樹的插入規則來找出即將被刪除的節點。程式碼如下; 
//儲存當前節點
		Node parent=root;//儲存當前節點父節點
		boolean isLeft=true;//記錄是否是左幾點
		//定位到將被刪除的節點 
		while(key!=curr.key){
			if(key<=curr.key){
				isLeft=true;//經過左節點
				if(curr.left!=null){
					parent=curr;
					curr=curr.left;
				}
			}else{
				isLeft=false;//經過右節點
				if(curr.right!=null){
					parent=curr;
					curr=curr.right;
				}
			}
			if(curr==null){
				return false;
			}
}


 在這斷程式碼中,首先定義了兩個引用,一個用來代表當前節點,一個用來代表當前節點的父節點,然後進入一個while迴圈,
迴圈裡,首先會根據key的比較來決定該往左走還是往右走,如果往左走,就說明當前節點是它父節點的左孩子了,然後把
ifLeft設定為true,如果往右走,同理就把isLeft設定為false(isLeft在後面會被用到)。現在,往下走得方向
已經確定了,然後該就判斷所經過的節點(即當前節點)是否為空,如果該節點的左右都為空,就說明你要刪的節點沒有找到,程式
將會返回false。如果如果當前節點左孩子不為空,就把當前節點等於它的左孩子,相當於又往下走了一步。正個尋找的過程,就是
不停的根據值判斷,不停的往下走,直到滿足條件,迴圈停止。這時,當前節點就儲存的是即將被刪除節點的引用,並且還知道被刪除的
父節點是誰,被刪除節點是它父節點的哪邊的孩紙都知道。
找到了被刪除的節點,下面就來看,要怎麼刪除它。
1.如果被刪除的節點是葉子節點,用程式碼表示就是
if(curr.left==null&&curr.right==null){
			if(curr==root){
				root=null;
			}else if(isLeft){
				parent.left=null;
			}else{
				parent.right=null;
			}
		}


可以看出,情況又被細分了三種,當被刪除節點即是葉子節點又是根節點,這是樹中就只有一個根節點了,就直接刪除
下面兩種是判斷的是,當前被刪除節點是其父節點哪邊的節點。

2.被刪除的節點只有左孩子節點
if(curr.right==null){
			if(curr==root){
				root=root.left;
			}else if(isLeft){
				parent.left=curr.left;
			}else{
				parent.right=curr.left;
			}
		}
當被刪除的節點只有一個孩子時,就只需要用它的孩子節點,把它自己給替換下去。具體的還是跟上面一樣,需要分三種情況
來考慮,如果是根節點,就直接把根節點指向根節點的左孩子,這樣一來,原來的根節點就無法訪問到,會被虛擬機器自動回收掉。
同理下面也一樣。

 
3.被刪除的節點只有右孩子節點,這種情況跟第二種情況類似
  
if(curr.left==null){
			if(curr==root){
				root=root.right;
			}else if(isLeft){
				parent.left=curr.right;
			}else{
				parent.right=curr.right;
			}
		}


 
4.被刪除的有兩個孩子節點,這種情況最複雜,因為要考慮到刪除之後順序不能亂。
所以這種型別的節點要刪除,如果直接刪,真個樹的大小順序就亂了,所以需要考慮,在樹中找到一個合適的節點來把這個節點
給替換掉,用這種方法來保持整個數的穩定。所以又一個問題又來了了,該找哪個節點來替換它?結論是,需要在樹中找出所有比
被刪除節點的值大的所有數,並在這些數中找出一個最小的數來。聽起來很拗,如果把它用圖形來描述的話,就是,從被刪除的節點出發
經過它的右節點,然後右節點最左邊的葉子節點就是我們要找的,它有一個專業名詞叫中序後繼節點。下面專門來寫一個方法來找它:
  
   
 public Node getSuccessor(Node delNode){  //引數為被刪除的節點
			//定義一個當前節點的引用,直接讓往下走一步,走到被刪除節點的右節點
			Node curr=delNode.right; 
			Node successor=curr;  //用來指向中級後續節點
			Node sucParent=null; //用來指向中級後續節點的父節點
			while(curr!=null){  
				sucParent=successor;
				successor=curr;
				curr=curr.left;
			}
			//迴圈停止,中級後續節點被找出
			if(successor!=delNode.right){
				//將後繼節點的子節點(只可能有右節點)替補到後繼節點的位置上
				sucParent.left=successor.right;
				//將被刪除的右孩子連線到後繼節點的右節點上
				successor.right=delNode.right;
				//將被刪除的左孩子連線到後繼節點的右節點上(就是用後繼節點替換掉被刪除的節點)
			}
			return successor;
		}



由於過程比較複雜,這裡用圖來表示


刪除節點的完成程式碼:

/**
	 * 刪除節點
	 * @param key
	 */
	public boolean delete(int key){
		Node curr=root;//儲存當前節點
		Node parent=root;//儲存當前節點父節點
		boolean isLeft=true;//記錄是否是左幾點
		//定位到將被刪除的節點 
		while(key!=curr.key){
			if(key<=curr.key){
				isLeft=true;//經過左節點
				if(curr.left!=null){
					parent=curr;
					curr=curr.left;
				}
			}else{
				isLeft=false;//經過右節點
				if(curr.right!=null){
					parent=curr;
					curr=curr.right;
				}
			}
			if(curr==null){
				return false;
			}
		}
		//如果被刪除節點是葉子節點
		if(curr.left==null&&curr.right==null){
			if(curr==root){
				root=null;
			}else if(isLeft){
				parent.left=null;
			}else{
				parent.right=null;
			}
		//如果被刪除節點只有左節點
		}else if(curr.right==null){
			if(curr==root){
				root=root.left;
			}else if(isLeft){
				parent.left=curr.left;
			}else{
				parent.right=curr.left;
			}
		//如果被刪除節點只有右節點
		}else if(curr.left==null){
			if(curr==root){
				root=root.right;
			}else if(isLeft){
				parent.left=curr.right;
			}else{
				parent.right=curr.right;
			}
		}else{
			Node successor=getSuccessor(curr);
			//將後繼節點與被刪除的父節點進行連線,完成整個替換過程
			if(curr==root){
				root=successor;
			}else if(curr==parent.left){
				parent.left=successor;
			}else{
				parent.right=successor;
			}
			successor.left=curr.left;
		}
		return true;
		
	}
	
	public Node getSuccessor(Node delNode){
		Node curr=delNode.right;
		Node successor=curr;
		Node sucParent=null;
		while(curr!=null){
			sucParent=successor;
			successor=curr;
			curr=curr.left;
		}
		if(successor!=delNode.right){
			//將後繼節點的子節點(只可能有右節點)替補到後繼節點的位置上
			sucParent.left=successor.right;
			//將被刪除的右孩子連線到後繼節點的右節點上
			successor.right=delNode.right;
			//將被刪除的左孩子連線到後繼節點的右節點上(就是用後繼節點替換掉被刪除的節點)
		}
		return successor;
	}