二叉樹中刪除一個節點
阿新 • • 發佈:2019-01-05
二叉樹的刪除可以算是二叉樹最為複雜的操作,刪除的時候要考慮到很多種情況:
1.被刪除的節點是葉子節點
2.被刪除的節點只有左孩子節點
3.被刪除的節點只有右孩子節點
4.被刪除的有兩個孩子節點
所以在刪除的時候,這4種情況都必須考慮進去,並且這4中情況之下,還會有細的劃分,下面就細說怎麼刪除。
在二叉樹中想要刪除一個節點,首先需要找到這個節點,由於二叉樹在插入節點的時候會遵循一個原則,就是利用節點的值來判斷
節點將被插入的位置(或者給節點加一個key,用key來判斷)。從根節點開始往下走,比當前節點小(或等於)的會被插入到
當前節點的左邊,比當前節點大的會被插入到當前節點的右邊。所以,根據根據二叉樹的插入規則來找出即將被刪除的節點。程式碼如下;
在這斷程式碼中,首先定義了兩個引用,一個用來代表當前節點,一個用來代表當前節點的父節點,然後進入一個while迴圈,
迴圈裡,首先會根據key的比較來決定該往左走還是往右走,如果往左走,就說明當前節點是它父節點的左孩子了,然後把
ifLeft設定為true,如果往右走,同理就把isLeft設定為false(isLeft在後面會被用到)。現在,往下走得方向
已經確定了,然後該就判斷所經過的節點(即當前節點)是否為空,如果該節點的左右都為空,就說明你要刪的節點沒有找到,程式
將會返回false。如果如果當前節點左孩子不為空,就把當前節點等於它的左孩子,相當於又往下走了一步。正個尋找的過程,就是
不停的根據值判斷,不停的往下走,直到滿足條件,迴圈停止。這時,當前節點就儲存的是即將被刪除節點的引用,並且還知道被刪除的
父節點是誰,被刪除節點是它父節點的哪邊的孩紙都知道。
找到了被刪除的節點,下面就來看,要怎麼刪除它。
1.如果被刪除的節點是葉子節點,用程式碼表示就是
可以看出,情況又被細分了三種,當被刪除節點即是葉子節點又是根節點,這是樹中就只有一個根節點了,就直接刪除
下面兩種是判斷的是,當前被刪除節點是其父節點哪邊的節點。
2.被刪除的節點只有左孩子節點
來考慮,如果是根節點,就直接把根節點指向根節點的左孩子,這樣一來,原來的根節點就無法訪問到,會被虛擬機器自動回收掉。
同理下面也一樣。
3.被刪除的節點只有右孩子節點,這種情況跟第二種情況類似
4.被刪除的有兩個孩子節點,這種情況最複雜,因為要考慮到刪除之後順序不能亂。
所以這種型別的節點要刪除,如果直接刪,真個樹的大小順序就亂了,所以需要考慮,在樹中找到一個合適的節點來把這個節點
給替換掉,用這種方法來保持整個數的穩定。所以又一個問題又來了了,該找哪個節點來替換它?結論是,需要在樹中找出所有比
被刪除節點的值大的所有數,並在這些數中找出一個最小的數來。聽起來很拗,如果把它用圖形來描述的話,就是,從被刪除的節點出發
經過它的右節點,然後右節點最左邊的葉子節點就是我們要找的,它有一個專業名詞叫中序後繼節點。下面專門來寫一個方法來找它:
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;
}