文章目录
- 前言
- 一、二叉排序树是什么?
- 二、BST的创建
- 1.正常搭建一颗树
- 2.添加结点
- 中序遍历BST
- 三、BST删除的三种情况
- 做删除算法前的准备工作
- 1.删除叶子节点
- 2.含一个子结点的结点
- 3.含两个子结点的结点
前言
前面所介绍的树都不能达到排序的效果 , 而本文要介绍的BST-二叉排序树是能轻松地将一颗树的各个结点值的大小进行左右划分, 话不多说,开始整活~
提示:以下是本篇文章正文内容,下面案例可供参考
一、二叉排序树是什么?
二、BST的创建
1.正常搭建一颗树
按常规步骤搭建一颗二叉树 , 同时注意 , 遍历方法采用的应该是中序遍历,在下文会有彩蛋
2.添加结点
我们在添加结点时 , 往期提到的添加节点的方法都是非常原始的手动添加 , 但在二叉排序树中 ,我们使用到了递归添加.
首先我们要为这个算法指定一些规则, 按照BST的原则 , 我们需要将比根节点小的元素都加到节点的左子节点上,将比根节点大的数都加到节点的右子树上 , 在同一层级上的所有节点 , 从左往右看, 是递增的
在具体的算法中 ,就拿左子树举例 , 当根节点的左子节点为空时 , 我们可以直接将节点加到左子节点上,当左子节点不为空时, 这是递归的作用就来了 ,我们可以递归寻找到左子树中左子节点为空的位置,再将节点插入
下面给出代码
代码如下(示例):
public void add(Node node){
if(node == null)
return;
if(node.value < this.value){ //加到左边
if(this.left == null){
this.left = node;
} else {
this.left.add(node); //递归添加
}
} else { //加到右边
if(this.right == null){
this.right = node;
} else {
this.right.add(node); //递归添加
}
}
}
中序遍历BST
这里需要讲一讲,当我们中序遍历BST时, 得到的结果是一个有序序列, 原因在于我们在搭建二叉树时,就是将小的元素放左边,大的元素放右边, 那么处于根节点位置的元素就是相较二者而言值居中的,因此在对BST使用中序遍历时, 得到的结果就是一个有序序列.
三、BST删除的三种情况
三种情况分别是 : 被删节点是1.叶子节点 2.仅含一个叶子节点 3.含2个叶子节点
做删除算法前的准备工作
在做删除操作之前 , 我们必须要查找到对应的节点和它的父节点
基本思路都是一个递归的思想
查找目标结点
BinarySortTree类中的search()方法
public Node search(Integer value){
if(this.root == null){
return null;
} else {
return this.root.search(value);
}
}
Node类中的search()方法
//查找要删除的结点
public Node search(Integer value){
if(value == this.value){
return this;
} else if(value < this.value){ //传入值小于根节点值
//向左找
if(this.left == null){
return null;
}
return this.left.search(value); //向左递归
} else { //传入值大于等于根节点值
if(this.right == null){
return null;
}
return this.right.search(value); //向右递归
}
}
查找目标结点的父节点
树类中的封装
public Node searchParent(Integer value){
if(this.root == null){
return null;
} else {
return this.root.searchParent(value);
}
}
节点类
//查找要删除结点的父节点
public Node searchParent(Integer value){
if((this.left!=null && this.left.value == value)||
(this.right!=null && this.right.value == value)){
return this;
} else {
if(this.left != null && value < this.left.value){
return this.left.searchParent(value);
}else if(this.right !=null && value > this.right.value){
return this.right.searchParent(value);
}else {
return null;
}
}
}
1.删除叶子节点
叶子节点的特点就是没有孩子节点 , 只有一个父节点, 因此对于叶子节点的删除
思路: 找到目标结点和它的父节点(如果存在的话), 然后确认它的其父结点的 左/右子结点,最后parent.left=null
或 parent.right=null
核心代码
//0.判断二叉排序树的结点个数是否为1 ,为一就结束查询
if(this.root.left == null || this.root.right == null){
return;
}
//1.找到要删除的结点
Node target = this.search(value);
//2.判空
if(target == null){
return;
}
//3.找到目标结点的父节点
Node parent = this.searchParent(value);
//4.确认目标结点是叶子节点 , 是父节点的左/右节点?
if(target.left == null && target.right == null){
if(parent.left != null && parent.left.value == value){
//说明target是其左子节点
parent.left = null; //置空
}
if(parent.right != null && parent.right.value == value){
//说明target是其左子节点
parent.right = null; //置空
}
}
2.含一个子结点的结点
思路 : 第一步和上面还是一样的,找到结点及其父节点. 然后确认它的子结点是左/右结点,它是它父节点的左/右节点 ,这样就会产生左右,左左,右左,右右
四种方案
if(target.left != null){
if(parent.left.value == value){
parent.left = target.left;
} else {
parent.right = target.left;
}
} else {
if(parent.right.value == value){
parent.right = target.right;
} else {
parent.left = target.right;
}
}
3.含两个子结点的结点
这里需要说一下的是 ,对于两个子结点的结点来说 , 需要一个辅助方法, 用于删除左子树中值最大的节点或右子树中值最小的节点
思路 : 第一步和上面还是一样的,找到结点及其父节点. 在目标节点的右子树中找到一个值最小的节点保存,删除找到的最小值节点,并将其赋值给目标节点 .
这么做其实同时删除了两个节点: 目标节点和最小值节点
public Integer delMinNodeOfRight(Node node){
Node target = node;
while(target.left != null){ //循环查找左节点,直到找到最小值
target = target.left;
}
//删除找到的最小值的节点,并将最小值返回
this.delNode(target.value);
return target.value;
}
Integer min = delMinNodeOfRight(target.right);
target.value = min;