二叉搜索树的实现

二叉搜索树

定义
  • 若任意节点的左子树不空,则左子树上的所有节点的值均小于它的根节点的值;

  • 若任意节点的右子树不空,则右子树上的所有节点的值均大于它的根节点的值;

  • 任意节点的左子树,右子树也分别为二叉查找树

  • 节点的值不能相同

  1. 如果节点的值存的是key,则key是不允许重复的
  2. 如果节点的值存的是key-value键值对,则key是不允许重复的,value是允许重复的。
  • 二叉搜索树的中序遍历是有许的
常见API实现(节点存的是key为例)
insert()

二叉搜索树的插入本质来说是插入到叶子节点上

  1. 如果是空树,让树的根节点指向要插入的新节点
  2. 查找合适的位置,插入元素(模拟查找过程),一个引用current标记查找节点,一个引用parentCurrent标记查找节点的父节点
  3. 用待插入的值和根节点进行比较
  4. 待插入的值和根节点相等,就插入失败(因为二叉搜索树不允许节点的值重复)
  5. 待插入的值比根节点的值小,就去根节点左子树找
  6. 待插入的值比根节点的值大,就去根节点右子树找
  7. 依次再比较待查找元素和左子树/右子树根节点的值,再去递归找左子树和右子树
  8. 循环结束current就指向null,当前元素要插入到parentCurrent的子树位置
  9. 在比较是插入到左子树还是右子树
  10. 待插入值小于parentCurrent的值,就插入到左子树
  11. 待插入值大于parentCurrent的值,就插入到右子树

**时间复杂度:**O(n)当整棵树退化为链表

search()

根据二叉搜索树的特性查找,避免了遍历整个树

  1. 用待查找的值和根节点值进行比较
  2. 待查找的值和根节点的值相等,说明找到返回true
  3. 待查找的值小于根节点的值,则去根的左子树去查找
  4. 待查找的值大于根节点的值,则去根节点的右子树去查找
  5. 依次再比较待查找元素和左子树/右子树根节点的值,再去递归找左子树和右子树
  6. 遍历到叶子节点,如果没有找到就返回false

**时间复杂度:**O(n) 当整棵树退化为链表

remove()
  1. 先查找待删除的值是否存在,存在再删除,不存在就删除失败
  2. 查找过程
  3. 用待删除的值和根节点进行比较
  4. 待删除的值和根节点的值比较
  5. 待删除的值和根节点的值相等,然后进行删除操作
  6. 待删除的值小于根节点的值,则去根节点的左子树查找
  7. 待删除的值大于根节点的值,则去根节点的右子树查找
  8. 依次在比较待删除值和左子树/右子树根节点的值,迭代下去
  9. 待删除的值存在,进行删除操作
  10. 分情况:
  11. 删除的是叶子节点
    1. 只有一个节点的树,直接让让根节点指向null
    2. 删的是父节点的左孩子
    3. 删的父节点的右孩子
  12. 删除的只有左或右子树的节点
    1. 删的是只有左子树的节点
      1. 删除的跟节点
      2. 删除的是父节点的左孩子
      3. 删除的父节点的右孩子
    2. 删的是只有右子树的节点
      1. 删除的根节点
      2. 删除的是父节点的左孩子
      3. 删除的是父节点的右孩子
  13. 删除的是有左和右子树的节点(替换法删除)
    • 方法1-替换最大值
      1. 找待删除节点左子树的最大值(替罪羊)-左子树的最右端
      2. 替换-待删除节点值替换为左子树的最大值(替罪羊)
      3. 删除左子树的最大值(替罪羊)-类似删除只有左子树的节点情况
    • 方法2-替换最小值
      1. 找待删除节点右子树的最小值(替罪羊)-右子树的最左端
      2. 替换-待删除节点值替换为右子树的最小值(替罪羊)
      3. 删除右子树的最小值(替罪羊)-类似删除只有右子树的情况

**时间复杂度:**O(n) 当整棵树退化为链表

实现

TreeNode类
public class TreeNode {
    Integer key;
    TreeNode left;
    TreeNode right;

    public TreeNode(Integer key){
        this.key=key;
    }
}
BinarySearchTree类
public class BinarySearchTree {
    TreeNode root;
    public BinarySearchTree(){
        this.root=null;
    }

    //中序遍历作为打印验证
    public void inOrder(TreeNode root){
        if (root==null){
            return;
        }

        inOrder(root.left);
        System.out.print(root.key+" ");
        inOrder(root.right);
    }

    public void insert(Integer key){
        if(this.root==null){
            this.root=new TreeNode(key);
            return;
        }
        //标识current的父节点
        TreeNode parentCurrent=null;
        //current寻找插入位置
        TreeNode current=this.root;
        int cmp=0;
        while (current!=null){
            cmp=key.compareTo(current.key);
            if (cmp==0){
                throw new RuntimeException("二叉搜索树不允许相同的key值");
            }else if (cmp<0){//往左插
                parentCurrent=current;
                current=current.left;
            }else{
                parentCurrent=current;
                current=current.right;
            }
        }
        TreeNode node=new TreeNode(key);
        if (cmp<0){
            parentCurrent.left=node;
        }else{
            parentCurrent.right=node;
        }
    }

    public boolean search(Integer key){
        if (this.root==null){
            return false;
        }
        TreeNode current=this.root;
        while (current!=null){
            int cmp=key.compareTo(current.key);
            if (cmp==0){
                return true;
            }else if(cmp<0){
                current=current.left;
            }else{
                current=current.right;
            }
        }
        return false;
    }
    /*
    * 1.删除的是叶子节点
    * 2.删除的是有一个孩子的节点
    * 3.删除的是有两个孩子的节点(替换删除)
    *   选择作左子树中最大的或者右子树中最小的
    *
    * */
    public boolean remove(Integer key) {
        if(this.root==null){
            return false;
        }
        //查找
        TreeNode parent=null;
        TreeNode current=this.root;
        while (current!=null){
            int cmp=key.compareTo(current.key);
            if (cmp==0){
                //删除
                removeInternal(current,parent);
                return true;
            }else if(cmp<0){
                parent=current;
                current=current.left;
            }else{
                parent=current;
                current=current.right;
            }
        }
        return false;
    }
    private void removeInternal(TreeNode node, TreeNode parent) {
        //删除的是叶子节点
        if(node.left==null&&node.right==null){
            if(this.root==node){//说明只有一个节点的树
                this.root=null;
            }else if (parent.left==node){ //删左孩子则去除
                parent.left=null;
            }else{//删有孩子则去除
                parent.right=null;
            }
        }else if(node.left!=null&&node.right==null){//删除只有左子树的节点
            if(this.root==node){
                this.root=node.left;
            }else if (node==parent.left){
                parent.left=node.left;
            }else{
                parent.right=node.left;
            }
        }else if(node.left==null&&node.right!=null){//删除只有右子树的节点
            if(root==node){
                root=node.right;
            }else if(node==parent.left){
                parent.left=node.right;
            }else{
                parent.right=node.right;
            }
        }else if(node.left!=null&&node.right!=null){
            //方法1-替换最大值
            //找待删除节点左子树中的最大值(替罪羊)-左子树的最右端
            /*
            * 为什么找左子树的最大值?
            * 左子树的最大值和待删除节点一替换,仍然满足二叉搜索树的性质
            * 左孩子小于根节点,右孩子大于根节点
            * */
            //从左孩子开始一直向右走到尽头
            TreeNode maxParent=node;
            TreeNode max=node.left;
            while (max.right!=null){
                maxParent=max;
                max=max.right;
            }

            //替换-把替罪羊的值赋给待删除的节点
            node.key=max.key;
            //删除替罪羊节点max(替罪羊节点一定没有右子树)
            //类似删除的只有左孩子的情况
            if (maxParent.left==max){
                maxParent.left=max.left;
            }else{
                maxParent.right=max.left;
            }


            //方法2-替换最小值(和替换最大值同理)
            //找右子树的最小值(替罪羊)-右子树的最左端
//            TreeNode minParent=node;
//            TreeNode min=node.right;
//
//            while (min.left!=null){
//                minParent=min;
//                min=min.left;
//            }
//
//            //替换
//            node.key=min.key;
//            //删除替罪羊(替罪羊节点没有左子树)
//            //类似于删除只右子树的节点
//
//
//            if (minParent.right==min){
//                minParent.right=min.right;
//            }else {
//                minParent.left=min.right;
//            }
            


        }
    }
}
测试类Test
public class Test {
    public static void main(String[] args) {
        BinarySearchTree bst=new BinarySearchTree();
        bst.insert(1);
        bst.insert(3);
        bst.insert(5);
        bst.insert(7);
        bst.insert(9);
        bst.insert(10);
        bst.insert(2);
        bst.insert(4);
        //中序遍历
        bst.inOrder(bst.root);
        System.out.println();

       System.out.println(bst.search(7));
       System.out.println(bst.search(6));

        System.out.println(bst.remove(1));
        bst.inOrder(bst.root);
        System.out.println();

        System.out.println(bst.remove(9));
        bst.inOrder(bst.root);
        System.out.println();

    }


}
结果

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值