首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >[Java数据结构与算法] 二叉搜索树(BinarySearchTree)

[Java数据结构与算法] 二叉搜索树(BinarySearchTree)

作者头像
木井巳
发布2025-12-16 09:50:54
发布2025-12-16 09:50:54
1000
举报

一、概念

二叉搜索树又称二叉排序树,是一种具有如下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

注意:二叉搜索树不能有重复的元素

如果对二叉搜索树进行中序遍历,所得到的就是一组有序的升序数据。

对上图的二叉搜索树中序遍历所得结果:0123456789。

它的代码结构:

代码语言:javascript
复制
public static class TreeNode {
    public int val;
    public TreeNode left;
    public TreeNode right;

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

二、二叉搜索树的查找操作

遍历二叉树,遍历每一个结点(cur)的时候,与所要查找数据(val)进行比较:

  • 若 cur.val 大于 val ,说明要查找的数据应在 cur 结点的左树,往左树查找;
  • 若 cur.val 小于 val ,说明要查找的数据应在 cur 结点的右树,往右树查找;
  • 若 cur.val 等于 val ,说明要查找的数据就是 cur 结点;

图示如下

代码实现

代码语言:javascript
复制
// 查找
public TreeNode search (int val) {
    TreeNode cur = root;

    while (cur != null) {
        if (cur.val < val) {
            // 往右子树查找
            cur = cur.right;
        } else if (cur.val > val) {
            // 往左子树查找
            cur = cur.left;
        } else {
            return cur;
        }
    }
    return null;
}

性能分析

  • 时间复杂度:最好情况(完全二叉树):O(logN);最坏情况(单分支):O(N)
  • 空间复杂度:O(1)

三、二叉搜索树的插入操作

先 遍历查找 搜索树,我们使用双指针来操作:

  • 若 要插入元素val 大于 当前遍历的元素cur.val ,就往 cur 的右树查找;
  • 若 要插入元素val 小于 当前遍历的元素cur.val ,就往 cur 的左树查找;
  • 若 要插入元素val 等于 当前遍历的元素cur.val ,就退出插入操作;

当 cur 为空时,prev刚好到达最后一个结点,此时可以开始插入新元素了。

插入之前要判断一下 prev的val 和 要插入元素val 的大小关系:

  • 若 prev.val > val ,val 就插入 prev 的左树;
  • 若 prev.val < val ,val 就插入 prev 的右树;

那问题来了,prev 是怎么移动的呢?

很简单:

  • 当 cur 为 根节点root 的时候,让 prev 为空;
  • 当 cur 开始遍历时,在 cur 往自己的左右树移动之前先让 prev 走到 cur 的位置

代码实现

代码语言:javascript
复制
// 插入
public void insert (int val) {
    TreeNode node = new TreeNode(val);

    if (root == null) {
        root = node;
        return;
    }

    TreeNode prev = null;
    TreeNode cur = root;

    // 遍历搜索树
    while (cur != null) {
        if (cur.val < val) {
            // 插入右子树
            prev = cur;
            cur = cur.right;
        } else if (cur.val > val) {
            // 插入左子树
            prev = cur;
            cur = cur.left;
        } else {
            // 二叉搜索树不可以插入两个相同的元素
            return;
        }
    }

    // 当cur为空时,就可以插入node了
    if (prev.val < val) {
        prev.right = node;
    } else {
        prev.left = node;
    }
}

性能分析

  • 时间复杂度:最好情况(完全二叉树):O(logN);最坏情况(单分支):O(N)
  • 空间复杂度:O(1)

四、二叉搜索树的删除操作

首先要找到要被删除的结点,然后删除结点。

删除结点的时候,有三种情况:

  1. cur 的左树为空;
  2. cur 的右树为空;
  3. cur 的左右树都不为空

1. cur 的左树为空且右树不为空时,我们需要判断 cur 是树根节点还是 prev 的左/右子树:

若 cur 是根结点,就让 cur 等于 cur.right;

若 cur 是 prev 的左树,就把 cur.right 给 prev.left;

若 cur 是 prev 的右树,就把 cur.right 给 prev.right;

2. cur 的右树为空且左树不为空时,我们同样需要判断 cur 是树根节点还是 prev 的左/右子树:

若 cur 是根结点,就让 cur 等于 cur.left;

若 cur 是 prev 的左树,就把 cur.left 给 prev.left;

若 cur 是 prev 的右树,就把 cur.left 给 prev.right;

3. cur 的左右树都不为空时,我们采用 替换删除 的策略:

从 cur 的左树找最右边的结点 / 右树找最左边的结点 (我们称之为 target,其上一个节点为 targetPrev)来替换 cur.val

然后我们需要判断:target 是 targetPrev 的左树还是右树:

若 target 是 targetPrev 的左树,就把 target.right 给 targetPrev.left;

若 target 是 targetPrev 的右树,就把 target.right 给 targetPrev.right;

如此一来,便完成了二叉搜索树的删除操作。

代码实现

代码语言:javascript
复制
// 删除
public void remove (int val) {
    // 找到要删除的结点
    TreeNode prev = null;
    TreeNode cur = root;

    while (cur != null) {
        if (cur.val < val) {
            prev = cur;
            cur = cur.right;
        } else if (cur.val > val) {
            prev = cur;
            cur = cur.left;
        } else {
            // 找到了,执行删除操作
            removeNode(prev,cur);
            return;
        }
    }
}
private void removeNode(TreeNode prev, TreeNode cur) {
    if (cur.left == null) {    // cur的左子树为空、右子树不为空
        if (cur == prev.left) {    // cur是prev的左子树
            prev.left = cur.right;
        } else if (cur == prev.right) {    // cur是prev的右子树
            prev.right = cur.right;
        } else {    // cur是根节点
            root = cur.right;
        }
    } else if (cur.right == null) {    // cur的右子树为空、左子树不为空
        if (cur == parent.left) {    // cur是prev的左子树
            prev.left = cur.left;
        } else if (cur == prev.right) {    // cur是prev的右子树
            prev.right = cur.left;
        } else {    // cur是根节点
            root = cur.left;
        }
    } else {    // cur的左右子树都不为空
        // 替换删除
        TreeNode targetPrev = cur;
        TreeNode target = cur.right;

        // 找到右子树最左边的结点
        while (target.left != null) {
            targetPrev = target;
            target = target.left;
        }

        // 替换
        cur.val = target.val;

        // 删除
        if (target == targetPrev.left) {
            targetPrev.left = target.right;
        } else {
            targetPrev.right = target.right;
        }
    }
}

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-10-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、概念
  • 二、二叉搜索树的查找操作
    • 代码实现
    • 性能分析
  • 三、二叉搜索树的插入操作
    • 代码实现
    • 性能分析
  • 四、二叉搜索树的删除操作
    • 代码实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档