Java实现二叉搜索树节点的删除

前言:

之前写过一篇关于二叉搜索树的博客:Java对二叉搜索树进行插入、查找、遍历、最大值和最小值的操作

 二叉查找树重要性质:

(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;

(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;

(3)左、右子树也分别为二叉排序树;

如图:


这次我想分享的是二叉搜索树中节点是如何删除的,删除节点是二叉搜索树常用的一般操作中最复杂的,删除节点要从查找要删除的节点开始入手 ,找到节点后,这个要删除的节点可能会有三种情况需要考虑:

1 该节点是叶节点(没有子节点)

2 该节点有一个子节点

3 该节点有两个子节点


情况1:删除没有子节点的节点

        要删除叶节点,只需改变该节点的父节点的对应子字段的值,由指向该节点改为null就可以了,要删除的节点仍然存在,但它已经不是树的一部分了。


因为Java语言有垃圾自动收集的机制,所以不需要非得把节点本身给删除。一旦Java意识到程序不再与这个节点有关联,就会自动把它清理出存储器。

首先找到要删除的节点,如果找到节点了,就从while循环中跳出,parent保存要删除的节点;如果找不到要删除的节点,就从方法中返回false,代码

如下:

// 假设树非空
		Node current = root;
		Node parent = root;
		boolean isLeftChild = true;
		
		while(current.iData != key) {
			parent = current;
			if(key < current.iData) {
				isLeftChild = true;
				current = current.leftChild;
			} else {
				isLeftChild = false;
				current = current.rightChild;
			}
			if(current == null)
				return false;
		} // end while

找到节点后,先要检查它是不是真的没有子节点。如果它没有子节点,还需要检查它是不是根。如果它是根的话,只需要把它置为null;这样就清空了树。否则,就把父节点的leftChild或rightChild字段置为null,断开父节点和那个要删除节点的连接。

if(current.leftChild == null && current.rightChild == null) {  // 表示要删除的节点是叶子节点,没有左右孩子
			if(current == root)
				root =null;
			else if(isLeftChild)    // 表示要删除的节点是左叶子节点
				parent.leftChild = null;
			else   // 表示要删除的节点是右叶子节点
				parent.rightChild = null;
			
			
		}

情况2:删除有一个子节点的节点

这个节点只有两个连接:连向父节点的和连向它唯一的子节点的。需要从这个序列中“剪断”这个节点,把它的子节点直接连到它的父节点上。这个过程要求改变父节点适当的引用(左子节点还是右子节点),指向要删除节点的子节点。



下面的代码用来处理有一个子节点的节点情况。有四种不同的情况:要删除节点的子节点可能有左子节点或右子节点,并且每种情况中的要删除节点也可能是自己父节点的左子节点和右子节点。

else if(current.rightChild == null) { // 表示要删除的节点只有左孩子
			if(current == root)
				root = current.leftChild;
			else if(isLeftChild)  // 表示要删除的结点是左子节点
				parent.leftChild = current.leftChild;
			else  // 表示要删除的结点是右子节点
				parent.rightChild = current.leftChild;
			
			
		} else if(current.leftChild == null) { // 表示要删除的节点只有右孩子
			if(current == root)
				root = current.rightChild;
			else if(isLeftChild)  // 表示要删除的结点是右子节点
				parent.leftChild = current.rightChild;
			else  // 表示要删除的结点是右子节点
				parent.rightChild = current.rightChild;
		} 

情况 3:删除有两个子节点的节点

如果要删除的节点有两个子节点,就不能只是用它的一个子节点代替它。


因此需要另一种方法:删除有两个子节点的节点,用它的中序后继来代替该节点(对每一个节点来说,比该节点的关键字值次高的节点是它的中序后继)。

所以我们的第一步是查找后继节点:

首先,程序找到初始节点的右子节点,它的关键字值一定比初始节点大。然后转到初始节点的右子节点的左子节点那里(如果有的话),然后到这个左子节点的左子节点,以此类推,顺着左子节点路径一直向下找。这个路径上的最后一个左子节点就是初始节点的后继。

如果初始节点的右子节点没有左子节点,那么这个右子节点本身就是后继。

下面是查找后继节点的代码:

private Node getSuccessor(Node delNode) {
		Node successorParent = delNode;
		Node successor = delNode;
		Node current = delNode.rightChild;
		
		while(current != null) {
			successorParent = successor;
			successor = current;
			current = current.leftChild;
		}
		if(successor != delNode.rightChild) {  // 如果后继节点是delNode右子节点的左后代
			successorParent.leftChild = successor.rightChild; // 把后继父节点的leftChild字段设置为successor的右子节点
			successor.rightChild = delNode.rightChild; // 把successor的rightChild字段置为要删除节点的右子节点
		}
		return successor;
	}

后继节点可能与current有两种位置关系,current就是要删除的节点。后继可能是current的右子节点,或者可能是current右子节点的左子孙节点。

1)后继节点是delNode的右子节点

只需两个步骤:

1、把current从它父节点的rightChild字段删掉(当然也可能是leftChild字段),把这个字段指向后继

2、把current的左子节点移出来,把它插到后继的leftChild字段

parent.rightChild = successor; // 把current从它父节点的rightChild字段移除,把这个字段置为successor
			
			successor.leftChild = current.leftChild; // 把current的左子节点从current移除,successor的左子节点指向的位置设为current的左子节点

2)后继节点是delNode右子节点的左后代

如果successor是要删除节点左子节点的左后代,执行删除操作需要以下四个步骤:

1、把后继父节点的leftChild字段设置为successor的右子节点

2、把successor的rightChild字段置为要删除节点的右子节点

3、把current从它父节点的rightChild(或leftChild)字段移除,把这个字段置为successor

4、把current的左子节点从current移除,successor的左子节点指向的位置设为current的左子节点

else {
			Node successor =getSuccessor(current);
			if(current == root)
				root =successor;
			else if(isLeftChild)
				parent.leftChild = successor; // 把current从它父节点的leftChild字段移除,把这个字段置为successor
			else
				parent.rightChild = successor; // 把current从它父节点的rightChild字段移除,把这个字段置为successor
			
			successor.leftChild = current.leftChild; // 把current的左子节点从current移除,successor的左子节点指向的位置设为current的左子节点
		}


以下是delete方法里面的完整代码:

public boolean delete(int key) {
		// 假设树非空
		Node current = root;
		Node parent = root;
		boolean isLeftChild = true;
		
		while(current.iData != key) {
			parent = current;
			if(key < current.iData) {
				isLeftChild = true;
				current = current.leftChild;
			} else {
				isLeftChild = false;
				current = current.rightChild;
			}
			if(current == null)
				return false;
		} // end while
		
		if(current.leftChild == null && current.rightChild == null) {  // 表示要删除的节点是叶子节点,没有左右孩子
			if(current == root)
				root =null;
			else if(isLeftChild)    // 表示要删除的节点是左叶子节点
				parent.leftChild = null;
			else   // 表示要删除的节点是右叶子节点
				parent.rightChild = null;
			
			
		} else if(current.rightChild == null) { // 表示要删除的节点只有左孩子
			if(current == root)
				root = current.leftChild;
			else if(isLeftChild)  // 表示要删除的结点是左子节点
				parent.leftChild = current.leftChild;
			else  // 表示要删除的结点是右子节点
				parent.rightChild = current.leftChild;
			
			
		} else if(current.leftChild == null) { // 表示要删除的节点只有右孩子
			if(current == root)
				root = current.rightChild;
			else if(isLeftChild)  // 表示要删除的结点是右子节点
				parent.leftChild = current.rightChild;
			else  // 表示要删除的结点是右子节点
				parent.rightChild = current.rightChild;
		} 
		
		
		else {
			Node successor =getSuccessor(current);
			if(current == root)
				root =successor;
			else if(isLeftChild)
				parent.leftChild = successor; // 把current从它父节点的leftChild字段移除,把这个字段置为successor
			else
				parent.rightChild = successor; // 把current从它父节点的rightChild字段移除,把这个字段置为successor
			
			successor.leftChild = current.leftChild; // 把current的左子节点从current移除,successor的左子节点指向的位置设为current的左子节点
		}
		return true;
	} // delete end


  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值