二叉树中删除一个节点
二叉树的删除能够算是二叉树最为复杂的操作,删除的时候要考虑到非常多种情况:
1.被删除的节点是叶子节点
2.被删除的节点仅仅有左孩子节点
3.被删除的节点仅仅有右孩子节点
4.被删除的有两个孩子节点
所以在删除的时候。这4种情况都必须考虑进去,而且这4中情况之下,还会有细的划分。以下就细说怎么删除。
在二叉树中想要删除一个节点,首先须要找到这个节点,因为二叉树在插入节点的时候会遵循一个原则。就是利用节点的值来推断
节点将被插入的位置(或者给节点加一个key,用key来推断)。从根节点開始往下走。比当前节点小(或等于)的会被插入到
当前节点的左边,比当前节点大的会被插入到当前节点的右边。所以。依据依据二叉树的插入规则来找出即将被删除的节点。
在这断代码中,首先定义了两个引用,一个用来代表当前节点,一个用来代表当前节点的父节点,然后进入一个while循环,
循环里。首先会依据key的比較来决定该往左走还是往右走。假设往左走,就说明当前节点是它父节点的左孩子了。然后把
ifLeft设置为true,假设往右走。同理就把isLeft设置为false(isLeft在后面会被用到)。如今,往下走得方向
已经确定了,然后该就推断所经过的节点(即当前节点)是否为空,假设该节点的左右都为空,就说明你要删的节点没有找到。程序
将会返回false。假设假设当前节点左孩子不为空,就把当前节点等于它的左孩子,相当于又往下走了一步。
能够看出。情况又被细分了三种,当被删除节点即是叶子节点又是根节点,这是树中就仅仅有一个根节点了,就直接删除
以下两种是推断的是,当前被删除节点是其父节点哪边的节点。
2.被删除的节点仅仅有左孩子节点
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;
}
posted on 2019-05-14 09:38 xfgnongmin 阅读(11102) 评论(2) 编辑 收藏 举报