二叉查找树--删除结点——参考学习

参考二叉树查找=–删除节点(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

    原文作者:二叉查找树
    原文地址: https://blog.csdn.net/u014067137/article/details/80350324
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞