二叉树的应用: 二叉排序树的结点删除(java)

本文详细解析了Java二叉排序树中结点的删除操作,涵盖三种核心情况:删除叶子结点、删除单子树结点及删除双子树结点。通过具体代码实现,展示了如何在保持树的排序属性的同时,有效移除目标结点。

Java 二叉排序树的结点删除

假设被删结点为p,其双亲节点为f。
共有三种情况:
1.p为叶子结点,删去p,并修改f的孩子域;
2.p只有左子树或者只有右子树,令p.lchild或p.rchild直接成为f的子树;
3.p的左右子树均不为空;

1.删除结点

//删除结点
public void delNode(int value) {
	if(root == null) {
		return;
	}
	else {
		//找到要删除的结点  targetNode
		BSTNode targetNode = search(value);
		//如果没有找到要删除的结点
		if(targetNode == null) {
			return;
		}

        //找到要删除的结点targetNode
		//去找到targetNode的父结点
		BSTNode parent = searchParent(value);
		
		//删除叶子结点
		if(targetNode.getLeft() == null && targetNode.getRight() == null) {
			//如果既是叶子节点又是根节点,则整棵树只有一个根结点,它无父结点
			//找到该结点且该结点无父结点——>根节点——>删除根结点
			if(parent==null){
				root = null;
				return;
			}
			//有父结点:判断targetNode 是父结点的左子结点,还是右子结点
			else{
				 //是左子结点
				if(parent.getLeft()!= null && parent.getLeft().getValue() == value) {
					parent.setLeft(null);
				}
				//是由子结点
				else if (parent.getRight() != null && parent.getRight().getValue() == value) {
					parent.setRight(null);
				}
			}	
		}
		//删除有两棵子树的结点
		else if (targetNode.getLeft() != null && targetNode.getRight() != null) {
			int minVal = delRightTreeMin(targetNode.getRight());
			targetNode.setValue(minVal);
		}
		// 删除只有一棵子树的结点
		else { 
			//如果要删除的结点有左子结点 
			if(targetNode.getLeft() != null) {
				if(parent != null) {
					//如果 targetNode 是 parent 的左子结点
					if(parent.getLeft().getValue() == value) {
						parent.setLeft(targetNode.getLeft());
					}
				    //  targetNode 是 parent 的右子结点
					else { 
						parent.setRight(targetNode.getLeft());
					} 
				} 
				else {
					root = targetNode.getLeft();
				}
			} 
			//如果要删除的结点有右子结点
			else {  
				if(parent != null) {
					//如果 targetNode 是 parent 的左子结点
					if(parent.getLeft().getValue() == value) {
						parent.setLeft(targetNode.getRight());
					} 
					//如果 targetNode 是 parent 的右子结点
					else { 
						parent.setRight(targetNode.getRight());
					}
				} 
				else{
					root = targetNode.getRight();
				}
			}
		}
	}
}

2.完整代码;

public class BinarySortTreeDemo {
	public static void main(String[] args) {
		BinarySortTree bst=new BinarySortTree();
		int []arr={7,3,10,12,5,1,9,0};
		//循环添加结点到二叉排序树
		for(int tmp:arr){
			bst.add(new BSTNode(tmp));
		}
		/*//中序遍历
		bst.infixOrder();
		
		for(int tmp:arr){
			System.out.println(tmp);
			System.out.println(bst.search(tmp));
			System.out.println(bst.searchParent(tmp));
		}*/
		bst.delNode(1);
		bst.infixOrder();
	}
}

class BinarySortTree{
	private BSTNode root;

	public BinarySortTree() {
		super();
		// TODO Auto-generated constructor stub
	}
	public BinarySortTree(BSTNode root) {
		super();
		this.root = root;
	}
	public BSTNode getRoot() {
		return root;
	}
	public void setRoot(BSTNode root) {
		this.root = root;
	}

	//添加结点的方法
	public void add(BSTNode node){
		if(root==null){
			root=node;
		}
		else{
			root.add(node);
		}
	}
	
	//中序遍历
	public void infixOrder(){
		if(root==null){
			System.out.println("该二叉树为空");
		}
		else{
			root.infixOrder();
		}
	}
	
	//查找要删除的结点
	public BSTNode search(int value){
		if(root==null){
			System.out.println("该二叉树为空");
		}
		return root.search(value);
	}
	
	//查找要删除的结点的父节点
	public BSTNode searchParent(int value){
		if(root==null){
			System.out.println("该二叉树为空");
		}
		return root.searchParent(value);
	}
	
	//删除结点
	public void delNode(int value) {
		if(root == null) {
			return;
		}
		else {
			//找到要删除的结点  targetNode
			BSTNode targetNode = search(value);
			//如果没有找到要删除的结点
			if(targetNode == null) {
				return;
			}
			//去找到targetNode的父结点
			BSTNode parent = searchParent(value);
			
			//删除叶子结点
			if(targetNode.getLeft() == null && targetNode.getRight() == null) {
				//如果既是叶子节点又是根节点,则整棵树只有一个根结点,它无父结点
				//找到该结点且该结点无父结点——>根节点——>删除根结点
				if(parent==null){
					root = null;
					return;
				}
				//有父结点:判断targetNode 是父结点的左子结点,还是右子结点
				else{
					 //是左子结点
					if(parent.getLeft()!= null && parent.getLeft().getValue() == value) {
						parent.setLeft(null);
					}
					//是由子结点
					else if (parent.getRight() != null && parent.getRight().getValue() == value) {
						parent.setRight(null);
					}
				}	
			}
			//删除有两棵子树的结点
			else if (targetNode.getLeft() != null && targetNode.getRight() != null) {
				int minVal = delRightTreeMin(targetNode.getRight());
				targetNode.setValue(minVal);
			}
			// 删除只有一棵子树的结点
			else { 
				//如果要删除的结点有左子结点 
				if(targetNode.getLeft() != null) {
					if(parent != null) {
						//如果 targetNode 是 parent 的左子结点
						if(parent.getLeft().getValue() == value) {
							parent.setLeft(targetNode.getLeft());
						}
					    //  targetNode 是 parent 的右子结点
						else { 
							parent.setRight(targetNode.getLeft());
						} 
					} 
					else {
						root = targetNode.getLeft();
					}
				} 
				//如果要删除的结点有右子结点
				else {  
					if(parent != null) {
						//如果 targetNode 是 parent 的左子结点
						if(parent.getLeft().getValue() == value) {
							parent.setLeft(targetNode.getRight());
						} 
						//如果 targetNode 是 parent 的右子结点
						else { 
							parent.setRight(targetNode.getRight());
						}
					} 
					else{
						root = targetNode.getRight();
					}
				}
			}
		}
	}
	
	/**
	 * 
	 * @param node 传入的结点(当做二叉排序树的根结点)
	 * @return 返回的 以node 为根结点的二叉排序树的最小结点的值
	 */
	public int delRightTreeMin(BSTNode node) {
		BSTNode target = node;
		//循环的查找左子节点,就会找到最小值
		while(target.getLeft() != null) {
			target = target.getLeft();
		}
		//这时 target就指向了最小结点
		//删除最小结点
		delNode(target.getValue());
		return target.getValue();
	}
}



class BSTNode{
	private int value;
	private BSTNode left;
	private BSTNode right;
	
	public BSTNode() {
		super();
	}

	public BSTNode(int value) {
		super();
		this.value = value;
	}
	
	
	
	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
	}

	public BSTNode getLeft() {
		return left;
	}

	public void setLeft(BSTNode left) {
		this.left = left;
	}

	public BSTNode getRight() {
		return right;
	}

	public void setRight(BSTNode right) {
		this.right = right;
	}

	@Override
	public String toString() {
		return "BSTNode [value=" + value + "]";
	}
	
	/**
	 * 向二叉排序树添加结点
	 * @param node
	 */
	public void add(BSTNode node){
		if(node==null){
			return;
		}
		if(node.value<this.value){
			if(this.left==null){
				this.setLeft(node);
			}
			else{
				this.left.add(node);
			}
		}
		else{
			if(this.right==null){
				this.setRight(node);
			}
			else{
				this.right.add(node);
			}
		}
	}
	
	//中序遍历
	public void infixOrder(){
		if(this.getLeft()!=null){
			this.left.infixOrder();
		}
		System.out.println(this);
		if(this.getRight()!=null){
			this.right.infixOrder();
		}
	}
	
	//查找要删除结点
	public BSTNode search(int value){
		if(this.value==value){
			return this;
		}
		//左子树查找
		if(value<this.value){
			if(this.left==null){
				return null;
			}
			else{
				return this.left.search(value);
			}
		}
		//右子树查找
		else{
			if(this.right==null){
				return null;
			}
			else{
				return this.right.search(value);
			}
		}
	}
	
	//查找要删除结点的父节点
	public BSTNode searchParent(int value){
		if((this.left!=null&&this.left.value==value)||(this.right!=null&&this.right.value==value)){
			return this;
		}
		//左子树查找
		if(value<this.value){
			if(this.left==null){
				return null;
			}
			else{
				return this.left.searchParent(value);
			}
		}
		//右子树查找
		else{
			if(this.right==null){
				return null;
			}
			else{
				return this.right.searchParent(value);
			}
		}
	}
}
<think>我们正在讨论二叉排序树(BinarySortTree),也称为二叉搜索树(BinarySearchTree)或二叉查找树。根据用户的问题,他想要了解二叉排序树节点的特性、特点及性质。二叉排序树是一种特殊的二叉树,它满足以下性质:1.若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值。2.若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值。3.它的左、右子树也分别为二叉排序树。因此,二叉排序树节点的特点可以总结为:-每个节点都有一个键值(key),用于比较和排序。-每个节点的键值大于其左子树中任意节点的键值,小于其右子树中任意节点的键值。-没有键值相等的节点(这一点在不同实现中可能有所不同,有些实现允许相等值放在左子树或右子树,但通常我们要求键值唯一)。二叉排序树的性质:1.二叉排序树的中序遍历(inordertraversal)会得到一个递增的有序序列。2.二叉排序树的查找、插入和删除操作的时间复杂度为$O(h)$,其中$h$是树的高度。最坏情况下(树退化为链表)时间复杂度为$O(n)$,平均情况下为$O(\logn)$(当树平衡时)。3.二叉排序树可以高效地支持动态集合操作,如查找、插入、删除、最小值、最大值、前驱和后继等。关于节点的删除操作(引用[2]提到了二叉排序树节点删除):二叉排序树节点的删除分为三种情况:a.要删除的节点是叶子节点:直接删除。b.要删除的节点只有一个子节点:将其父节点指向该节点的指针指向其子节点。c.要删除的节点有两个子节点:通常用其右子树的最小节点(或左子树的最大节点)代替该节点,然后删除那个最小(或最大)节点(这个节点最多只有一个子节点,因此可以按照情况b删除)。下面用代码说明二叉排序树节点的结构(参考引用[1]中二叉树的定义):```javapublicclassTreeNode{intval;//节点的值TreeNodeleft;//左子节点TreeNoderight;//右子节点TreeNode(intx){val=x;}}```二叉排序树节点的特性体现在其值(val)与左右子树的关系上:-左子树的所有节点值<当前节点值-右子树的所有节点值>当前节点值因此,在二叉排序树中,节点的位置由其值的大小关系决定,从而形成有序结构。最后,我们生成相关问题:1.二叉排序树的中序遍历为什么是有序的?2.二叉排序树的查找、插入和删除操作如何实现?3.二叉排序树在什么情况下会退化为链表?如何避免?4.平衡二叉排序树(如AVL树、红黑树)是如何保证平衡性的?</think>### 二叉排序树节点的特性及性质 二叉排序树(Binary Sort Tree)是一种特殊的二叉树结构,其节点具有以下核心特性和性质: #### 一、节点特性 1. **有序性** 任意节点满足: - **左子树**所有节点值 $\leq$ 当前节点值 - **右子树**所有节点值 $\geq$ 当前节点值 (具体实现中可定义严格不等或包含等于) 2. **递归结构** 每个节点的左右子树**本身也是二叉排序树**,满足相同的有序性规则[^1]。 3. **键值存储** 节点存储可比较的键值(通常为数值或可排序对象),通过`val`字段实现: ```java class TreeNode { int val; // 核心键值 TreeNode left; // 左子节点指针 TreeNode right; // 右子节点指针 } ``` #### 二、核心性质 1. **中序遍历有序性** 对二叉排序树进行**中序遍历**(左-根-右),必然得到**升序序列**。这是二叉排序树的本质特征[^1][^2]。 2. **高效查找** 查找时间复杂度为$O(h)$($h$为树高): - 平衡状态下:$O(\log n)$ - 最坏情况(退化为链表):$O(n)$ 3. **动态维护** 支持高效动态操作: - **插入**:新节点按有序性插入适当位置 - **删除**(三类情况[^2]): - 叶子节点:直接删除 - 单子节点:父节点直连其子节点 - 双子节点:用前驱/后继节点替换后删除 #### 三、结构示例 ```mermaid graph TD A(50) --> B(30) A --> C(70) B --> D(20) B --> E(40) C --> F(60) C --> G(80) ``` *特性验证*: - 左子树值(20,30,40)< 根节点50 - 右子树值(60,70,80)> 根节点50 - 中序遍历:20 → 30 → 40 → 50 → 60 → 70 → 80(升序) #### 四、应用优势 1. **检索效率**:优于无序数组的$O(n)$查找 2. **动态操作**:插入/删除效率高于静态有序数组 3. **范围查询**:可快速定位值域区间内的节点 > **关键点**:二叉排序树的核心在于**中序遍历的有序性**,这一性质使其成为高效动态查找结构的基础。但当树严重不平衡时性能退化,此时需引入**平衡二叉排序树**(如AVL树、红黑树)。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值