二叉搜索树(binary search tree)的删除操作(非递归实现)

概述

历史

二叉搜索树最早是由Bernoulli兄弟在18世纪中提出的,但是真正推广和应用该数据结构的是1960年代的D.L. Gries。他的著作《The Science of Programming》中详细介绍了二叉搜索树的实现和应用。

在计算机科学的发展中,二叉搜索树成为了一种非常基础的数据结构,被广泛应用在各种领域,包括搜索、排序、数据库索引等。随着计算机算力的提升和对数据结构的深入研究,二叉搜索树也不断被优化和扩展,例如AVL树、红黑树等。

特性

二叉搜索树(也称二叉排序树)是符合下面特征的二叉树:

  1. 树节点增加 key 属性,用来比较谁大谁小,key 不可以重复

  2. 对于任意一个树节点,它的 key 比左子树的 key 都大,同时也比右子树的 key 都小,例如下图所示

轻易看出要查找 7 (从根开始),只需三次比较

  • 与 4 比,较之大,向右找

  • 与 6 比,较之大,继续向右找

  • 与 7 比,找到

查找的时间复杂度与树高相关,插入、删除也是如此。

  • 如果这棵树比较平衡,那么时间复杂度均是 O(log{N})

  • 当然,这棵树如果不平衡(左右高度相差过大)下图,那么这时是最糟的情况,时间复杂度是 O(N)

 实现

定义节点

public class BSTTree1 {
    static class BSTNode {
        int key; // 若希望任意类型作为 key, 则后续可以将其设计为 Comparable 接口
        Object value;
        BSTNode left;
        BSTNode right;

        public BSTNode(int key) {
            this.key = key;
            this.value = key;
        }

        public BSTNode(int key, Object value) {
            this.key = key;
            this.value = value;
        }

        public BSTNode(int key, Object value, BSTNode left, BSTNode right) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
        }
    }
    BSTNode root;//根节点
}

这里查询以及put操作不再赘述 主要来分析删除操作的流程

删除

要删除某节点(称为 D),必须先找到被删除节点的父节点,这里称为 Parent

  1. 删除节点没有左孩子,将右孩子托孤给 Parent

  2. 删除节点没有右孩子,将左孩子托孤给 Parent

  3. 删除节点左右孩子都没有,已经被涵盖在情况1、情况2 当中,把 null 托孤给 Parent

  4. 被删除节点左右孩子双全 可以将它的后继节点(称为 S)托孤给 Parent,设 S 的父亲为 SP,又分两种情况 

          (1)被删除节点和后继节点相邻,直接把后继节点顶到被删除节点的位置即可

          (2)不相邻,先处理后事,即处理后继节点的右孩子(为什么是右孩子,不是左孩子呢?因为后继节点会找被删除节点key值最近的节点,如果后继节点s有左孩子,那么就不可能找到的是s,而是s的左孩子 这里比较绕,可以看下图多思考几遍)

 代码实现

托孤方法

/**
     * 托孤方法
     * @param parent  被删除节点的父亲
     * @param deleted 被删除节点
     * @param child   被顶上去的节点
     */
    private void shift(BSTNode parent,BSTNode deleted,BSTNode child){
        if(parent == null){
            root = child;
        }
        if(parent.left == deleted){
            parent.left = child;
        }else{
            parent.right = child;
        }
    }
public Object delete(int key){
        BSTNode p = root;
        BSTNode pParent = null;
        while(p != null){
            if(key < p.key){
                pParent = p;
                p = p.left;
            }else if(key > p.key){
                pParent = p;
                p = p.right;
            }else{
                break;
            }
        }
        if(p == null){
            return null;
        }
        //p为被删除节点 pParent为被删除节点的父亲
        if(p.left == null){
            //情况一 被删除节点有右孩子没有左孩子 只需把被删除节点的右孩子托孤给父亲
            //情况三 被删除节点为叶子节点 既没有左孩子也没有右孩子 直接走了情况一的逻辑
            shift(pParent,p,p.right);
        }else if(p.right == null){
            //情况二 被删除节点有左孩子没有右孩子 只需把被删除节点的左孩子托孤给父亲
            shift(pParent,p,p.left);
        }else{
            //情况四 被删除节点左右孩子双全
            BSTNode s = p.right;
            BSTNode sParent = p;
            while(s.left != null){
                sParent = s;
                s = s.left;
            }
            //1.被删除节点和后继节点s相邻 直接顶上去
            //2.不相邻 处理后事
            if(sParent != p){
                shift(sParent,s,s.right);//处理s的右孩子
                s.right = p.right; //认亲被删除节点的右孩子  如果相邻无需认亲右孩子
            }
            //3.顶替被删除节点
            shift(pParent,p,s);
            s.left = p.left; //认亲被删除节点的左孩子 无论是否相邻都需认亲
        }
        Object value = p.value;
        p = null;//help GC
        return value;
    }

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值