参考二叉树查找=--删除节点(JAVA实现),进一步理解删除结点,感谢博主的分享
删除结点
删除结点存在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