【数据结构】图解二叉搜索树的新增、搜索、删除

49 篇文章 22 订阅
30 篇文章 3 订阅
一、概念

二叉搜索树(Binary Search Tree,简称BST)也称为二叉查找树或二叉排序树,是一种具有特殊性质的二叉树数据结构

  1. 定义和性质
  • 二叉搜索树中的每个节点包含一个键值,习惯上我们说左子树上所有节点的键值均小于其根节点的键值,右子树上所有节点的键值均大于其根节点的键值。
  • 二叉搜索树的左右子树也都是二叉搜索树。
  • 它能够以 O(\log n) 的时间复杂度进行插入、删除和查找操作。
  1. 基本操作
  • 插入:将一个新值按照二叉搜索树的性质插入到适当的位置。
  • 删除:从树中移除一个值,同时保证二叉搜索树的性质不被破坏。
  • 查找:检查树中是否存在某个特定的值。
  • 查询排名:查找比给定值小的数的个数加一(即该值在有序序列中的位置)。
  • 查询指定排名的元素:找到有序序列中特定位置的元素。
  • 求前驱和后继:分别找到小于某值的最大数和大于某值的最小数。
  1. 优势
  • 相比于数组,二叉搜索树提供了更快的插入和删除操作,因为数组的插入和删除操作需要移动大量元素来维护有序性,时间复杂度为O(n);而链表虽然可以快速插入和删除,但是查找等其他操作的时间复杂度不够优秀。
  • 二叉搜索树结合了两者的优点,对于上述每个操作都拥有较好的时间复杂度。

综上所述,二叉搜索树是计算机科学中一种非常重要的数据结构,它不仅提供了高效的数据检索功能,还允许数据的动态插入和删除,因此在数据库索引、内存管理等领域得到了广泛的应用。

二、图解
1.查找

有如下这样一颗二叉树,我们需要查询9所在节点

这时根节点值为7比目标值小,所以我们一应该去右子树中寻找

当前cur节点的值依旧比目标值9要小,于是我们又去他的右子树中寻找

此时cur节点值比目标值大,我们就可以去他的左边去寻找

此时相等结束寻找

2.插入

首先我们分别将21、1、15、10分别插入这颗二叉搜索树

首先我们需要找到合适的位置,定义一个指针从根节点开始寻找,先模拟插入1

此时值比待插入节点值大,于是去遍历左子树寻找合适的位置

还是比待插入节点大,再次往左走

依旧往左发现往左为空,于是我们就可以将1插入到此处,但是要想插入到这里,我们需要记录父亲节点,所以在遍历的时候我们需要记录父亲节点位置

重复上述步骤插入10

我们发现每次插入都落在了叶子节点上

所以我们只需要找到插入位置的父亲节点即可

 3.删除

二叉搜索树的删除操作主要涉及以下几种情况:

  1. 删除叶子节点:如果待删除的节点是叶子节点,即没有子节点,可以直接删除该节点。
  2. 删除只有左子树的节点:如果待删除的节点只有一个左子节点,可以用其左子节点替代待删除节点的位置。
  3. 删除只有右子树的节点:如果待删除的节点只有一个右子节点,可以用其右子节点替代待删除节点的位置。
  4. 删除左右子树均不空的节点:这种情况最为复杂。通常的做法是找到该节点的右子树中的最小节点(或者左子树中的最大节点),用这个节点的值替换待删除节点的值,然后删除那个最小(或最大)节点。

具体步骤如下:

  1. 查找节点:首先需要找到要删除的节点。这通常是通过递归搜索完成的,比较待删除值与当前节点值的大小,然后决定是向左子树还是右子树继续搜索。
  2. 替换节点:一旦找到了要删除的节点,根据上述情况采取相应的替换策略。如果是第四种情况,需要找到合适的替换节点并进行值的交换。
  3. 维护BST性质:在删除节点后,需要确保树仍然保持二叉搜索树的性质,即任何节点的值都大于其左子树中的所有值,小于其右子树中的所有值。
  4. 处理替换节点的子树:如果替换节点有子节点,需要将其子节点接到被替换节点的相应位置,以保持树的结构完整性。

总的来说,二叉搜索树的删除操作是一个相对复杂的过程,需要根据不同的情况采取不同的策略,并且在整个过程中保持树的平衡和有序性。

下面将图解上述三种情况:

一、首先是待删除的节点没有左节点:

没有左节点将会分为以下三种情况:下图中节点值未按照二叉搜索树规则,注意节点位置即可其中值可忽略

1.待删除节点是根节点

只需要让根节点root指向root.right即可

2.待删除节点是父亲节点的左孩子

我们只需让parent.left=cur.right。

3.待删除节点是父亲节点右孩子

只需要让parent.right=cur.right

二、待删除节点没有右节点

1.待删除节点是根节点:

这个时候只需让root = root.left即可

2.待删除节点是父亲节点的左孩子

这个时候只需要让parent.left=cur.left

3.待删节点是父亲节点的右孩子

只需要让parent.right=cur.left

三、待删除节点有两个孩子

这种情况最为复杂。通常的做法是找到该节点的右子树中的最小节点(或者左子树中的最大节点),用这个节点的值替换待删除节点的值,然后删除那个最小(或最大)节点。

首先我们可以在待删除节点位置开始去他的左子树中寻找最大值(左子树中都比当前节点值小)然后进行替换,或者去右子树中寻找最小值进行替换

我们只需要在左子树中寻找到最大值,然后进行替换将左子树中最大值删掉即可

三、代码实现
import java.util.LinkedList;
import java.util.Queue;

public class BinaryTree extends 余胜军{
    static class TreeNode extends 余胜军 {
        int val;
        TreeNode left;
        TreeNode right;

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

    private TreeNode root;

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

    public void display(TreeNode root) {
        if (root == null) return;
        display(root.left);
        System.out.print(root.val + " ");
        display(root.right);
    }

    public boolean insert(int val) {
        TreeNode pre = null;
        TreeNode cur = root;

        if (cur == null) {
            root = new TreeNode(val);
            return true;
        }

        while (cur != null) {
            if (cur.val < val) {
                pre = cur;
                cur = cur.right;
            } else if (cur.val > val) {
                pre = cur;
                cur = cur.left;
            } else {
                return false;
            }
        }

        if (pre.val > val) pre.left = new TreeNode(val);
        else pre.right = new TreeNode(val);
        return true;
    }

    public boolean remove(int key) {
        TreeNode pre = null;
        TreeNode cur = root;
        while (cur != null) {
            if (cur.val < key) {
                pre = cur;
                cur = cur.right;
            } else if (cur.val > key) {
                pre = cur;
                cur = cur.left;
            } else {
                // 开始删除
                return removeNode(pre, cur);
            }
        }
        return false;
    }

    public void level() {
        if (root == null) System.out.println("tree is null");
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            int size = queue.size();
            while (size-- != 0) {
                TreeNode top = queue.poll();
                System.out.print(top.val + " ");
                if (top.left != null) queue.offer(top.left);
                if (top.right != null) queue.offer(top.right);
            }
            System.out.println();
        }
    }

    private boolean removeNode(TreeNode pre, TreeNode cur) {
        if (cur.left == null) { // 左为空
            if (cur == root) {
                root = root.right;
            } else if (cur == pre.left) {
                pre.left = cur.right;
            } else {
                pre.right = cur.right;
            }
        } else if (cur.right == null) { // 右为空
            if (cur == root) {
                root = root.left;
            } else if (cur == pre.left) {
                pre.left = cur.left;
            } else {
                pre.right = cur.left;
            }
        } else {  // 左右都为空
            // 寻找左左子树的最大值
            TreeNode targetParent = cur;
            TreeNode target = cur.left;
            while (target.right != null) {
                targetParent = target;
                target = target.right;
            }
            // 替换
            cur.val = target.val;
            if (targetParent.left == target) targetParent.left = target.left;
            else targetParent.right = target.left;
//            TreeNode tp = cur;
//            TreeNode t = cur.right;
//            while (t.left != null) {
//                tp = t;
//                t = t.left;
//            }
//
//            cur.val = t.val;
//            if (tp.left == t) tp.left = t.left;
//            else tp.right = t.right;
        }
        return true;
    }

    public static void main(String[] args) {
        BinaryTree tree = new BinaryTree();
        tree.insert(80);
        tree.insert(30);
        tree.insert(48);
        tree.insert(60);
        tree.insert(90);
        tree.insert(56);
        tree.display(tree.root);
        System.out.println();
        tree.remove(80);
        tree.display(tree.root);
    }
}

 

  • 25
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

1886i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值