Java数据结构之二叉排序树-BST的基本算法(创建与删除的三种情况)


前言

前面所介绍的树都不能达到排序的效果 , 而本文要介绍的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=nullparent.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;

猜你喜欢

转载自blog.csdn.net/qq_45596525/article/details/110321161