Java二叉搜索树

本文详细介绍了二叉搜索树的概念,包括查找、插入和删除节点的过程。查找节点时,根据节点值大小进行左右分支遍历;插入节点确保保持二叉搜索树性质;删除节点分为三种情况,考虑左右子节点是否为空。二叉搜索树在最坏情况下时间复杂度为O(N),但理想情况下可达到O(logN)。为了提高查找效率,可以使用平衡树如AVL或红黑树。
摘要由CSDN通过智能技术生成

定义:当一棵二叉树满足任意一个节点的左子节点的值<自己的值<节点右子节点的值的时候,并且此二叉树的中序遍历为有序的时候,这棵二叉树被称为二叉搜索树,又称为二叉查找树。

有关二叉搜索树的一些概念:查找一个节点,时间复杂度为O(n),这是最坏的情况,单分支的情况是查找效率最低的情况,理想情况下面,时间复杂度为O(logN),N为节点个数。

给定一棵二叉搜索树,根据节点值大小排序所需时间复杂度是线性的

 如图为一棵二叉搜索树:

查找某个节点:

例如此时需要查找11,需要一个cur节点指向根节点,当目标值大于cur的值的时候,指针往右走,当目标值小于当前值的时候,指针往左走,否则返回当前的指向。

代码实现:

static class TreeNode {
        public int val;
        public TreeNode left;
        public TreeNode right;

        public TreeNode(int val) {
            this.val = val;
        }
    }
    public TreeNode root=null;

    public TreeNode search(int val){
        TreeNode cur=root;
        while (cur!=null){
            if(cur.val<val){
               cur=cur.left;
            }
            else if(cur.val>val){
                cur=cur.right;
            }
            else {
                return cur;
            }
        }
        return null;
    }

二:新增节点

如果root为null,那么直接插入到根节点当中

其次,每个节点都会插入到叶子节点的位置

如图,这种情况,需要插入一个10:

首先,定义一个parent指向cur走过的上一个节点。

当cur为null时候,就可以填充值了。

当cur的值小于待插入的值的时候,parent指向cur,cur指向自己的右子节点。

当cur的值大于待插入的值的时候,  parent指向cur,cur指向自己的左节点。

 

如图:此时parent指向11,cur也指向11,当cur继续往左走的时候,cur为NULL了,此时,由于parent指向了11,比10大,因此把10插入到11的右侧。

代码实现:

public void insert(int val){
        if(root==null){
            root=new TreeNode(val);
            return;
        }
        TreeNode parent=null;
        TreeNode cur=root;
        while (cur!=null){
            if(cur.val<val){
                parent=cur;
                cur=cur.right;
            }
            else if(cur.val>val){
                parent=cur;
                cur=cur.left;
            }else {
                throw new RuntimeException("不能插入相同数据");
            }
        }
        TreeNode node=new TreeNode(val);
        if(parent.val<val){
            parent.right=node;
        }else {
            parent.left=node;
        }
    }


二叉搜索树的删除节点:3种情况:

A.待删除节点的左子节点为null:

        ①待删除节点为root时候,直接root=cur.right;

       

        ②待删除节点位于parent的左枝时候,

       待删除节点的parent节点的左子区域指向待删除节点的右枝

parent.left=cur.right;

 ③待删除节点位于parent右枝的时候:

   parent.right=cur.right;

B.待删除节点的右子树为NULL时候,这里就不画图了,直接写结论:

简单来说,就是替换parent的左枝,右枝的指向。

if(cur.right==null){

if(root==parent){

root=cur.left;

}else if(parent.left==cur){

  parent.left=cur.left;

}else if(parent.right==cur){

parent.right=cur.left;

}

}

C、左右都不为NULL的情况

这种时候,就要从待删除节点的左枝或者右枝寻找,找到一个值(target),要么为左枝当中最大的值,要么为右枝当中最小的值:即:左枝最右边的子树或者右枝最左边的子树。

用这个值替换掉被删除的节点,同时删除target原来的值所在的节点。

如果选择右边最左的子树的情况的话,就要定义一个辅助指针指向cur,称这个指针为targetParent,那么target就是cur.right。此时,就需要target一路向左走,当target的左枝为NULL时候,说明找到了target。那么删除的话,要分target位于target的左枝或者右枝的情况来删除。

 

代码实现:

 /**
     * 删除值
     * 待删除的值@param val
     */
    public void remove(int val) {
        if (root == null) {
            throw new RuntimeException("树为空,无法删除");
        }
        TreeNode cur = root;
        TreeNode parent = null;
        while (cur != null) {
            if (cur.val < val) {
                parent = cur;
                cur = cur.right;
            } else if (cur.val > val) {
                parent = cur;
                cur = cur.left;
            } else {
                //找到需要删除的节点了,执行需要删除的逻辑
                removeTheNode(parent, cur);
                return;
            }
        }
    }

    /**
     * 删除对应的节点
     * 父亲节点@param parent
     * 当前节点@param cur
     */
    private void removeTheNode(TreeNode parent, TreeNode cur) {
        if (cur.left == null) {
            if (cur == root) {
                root = cur.right;
            } else if (parent.left == cur) {
                parent.left = cur.right;
            } else if (parent.right == cur) {
                parent.right = cur.right;
            }
        } else if (cur.right == null) {
            if (cur == root) {
                root = cur.left;
            } else if (parent.right == cur) {
                parent.right = cur.left;
            } else if (parent.left == cur) {
                parent.left = cur.left;
            }
        } else {
            TreeNode targetParent = cur;
            TreeNode target = cur.right;

            while (target.left != null) {
                targetParent = target;
                target = target.left;
            }
            cur.val = target.val;
            //当待删除节点位于父亲节点左枝:
            if (target == targetParent.left) {
                targetParent.left = target.right;
            }
            //当待删除节点位于父亲节点右枝
            else if (target == targetParent.right) {
                targetParent.right = target.right;
            }
        }
    }

如果问起二叉搜索树的查找时间复杂度,那么就应当为O(N),因为i当二叉查找树为仅仅左枝/右枝存在的时候,就会退化为链表的结构,查找效率降低了许多,因此将会引入avl树,红黑树等等的来提升查找的效率。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值