二叉搜索树

二叉搜索树在集合类中对应到TreeSet,TreeMap的底层实现,它属于一种特殊的二叉树,对比于普通的二叉树,它的特殊之处在于左子树的所有值都小于根节点,而右子树的所有值都大于根节点。所以我们根据中序遍历二叉搜索树就可以得到一个有序的序列。顾名思义,二叉搜索树的最大的用处在于查找,如果我们要查找一个元素,就把它和根节点进行比较,如果小于根节点,就去左边找,如果大于根节点,就去右边找。大大提高了查找效率,一般情况下时间复杂度可达O(logN),除非这颗树二叉搜索树特别的不一般,那就是只有单边树的情况,这样的查找类似于遍历链表,时间复杂度为O(N)。为了防止这种特殊情况,进一步又引入了两种特殊的二叉搜索树——AVL树和红黑树。
AVL树是一种严格平衡的二叉搜索树,要求就是必须平衡,但是这样也就导致了插入删除操作之后会涉及到重新调整树的结构,会很麻烦
红黑树在AVL树的基础上又进一步做了改善,要求不是那么严格,只需要比较平衡就可以了,实际上TreeMap和TreeSet的底层实现就是红黑树。

二叉搜索树的基本操作

  1. 插入元素
    思路就是先找到待插入元素的合适位置,然后再进行插入
public boolean insertkey(int key){
        //首先判断是否是空树
        if(root == null){
            root = new Node(key);//让root指向一个新的节点,这个新节点的key就是插入的这个元素
            return true;
        }
        //当root不为null的时候有也就是不是空树的时候
        Node parent = null;//首先定义一个parent来保留cur的父节点的记录,因为cur为null了代表找到
        //插入位置了,需要定义一个parent来进行插入
        Node cur = root;
        while(cur!=null){
            if(key>cur.key){
                parent = cur;
                cur = cur.right;//代表需要把这个元素往右插
            }else if(key<cur.key){
                parent = cur;
                cur = cur.left;
            }else{//此时代表当前key = cur.key.重复了,就直接返回false
                return false;
            }
        }
        //上面循环走完cur已经为null了,代表此时找到了插入位置,也就是parent的子树
        if(key>parent.key){
            parent.right = new Node(key);//创建一个新节点插入到parent的右边,这个新节点也就是key
        }else{
            parent.left = new Node(key);
        }
        return true;
    }
  1. 查找元素
 public  Node find(int key){//1.查找元素,如果找到则返回当前节点的地址
        Node cur = root;//让cur从根节点出发
        while (cur!=null){
            if(key>cur.key){//说明在当前节点的右边
                cur = cur.right;
            }else if(key<cur.key){//说明在当前节点的左侧
                cur = cur.left;
            }else{//此时说明找到了
                return cur;
            }
        }
        //循环走完cur已经为null了,此时还没找到,就返回null
        return null;
    }
  1. 删除元素
    删除元素比较复杂。
    ①.待删除元素的左子树不为null,右子树为null的时候
    此时又要判断待删除节点是它的父节点的左子树还是右子树,如果是左子树的话就让它的父节点的left= 待删除节点的left。如果是右子树的话就让它的父节点的right = 待删除节点的left
    ②.待删除节点的左子树为null,右子树不为null
    此时同样判断待删除节点是它的父节点的左子树还是右子树,具体操作和上面一样
    ③.待删除节点的左右子树都不为null
    仍然需要判断是它的父节点的左子树还是右子树,具体操作也一样,以左子树为例,就是先找到待删除节点右子树中的最小值,或者左子树中的最大值,然后覆盖到待删除节点的位置,最后再删除这个最小值或者最大值就可以了,因为如果在右子树中找到了最小值,那么此时这个最小值一定没有左子树,所以删除方法和上面的情况一样。在左子树中找最大值也是一样。
 public boolean remove(int key){
        //如果key在树中存在,就删除成功,不存在就删除失败
        //先找到要删除的节点的位置,再进行删除
        //找到待删除元素之后,再去判断是哪种情况
        Node cur = root;
        Node parent = null;
        while(cur!=null){
            if(key>cur.key){
                parent = cur;
                cur = cur.right;
            }else if(key<cur.key){
                parent = cur;
                cur = cur.left;
            }else{//此时说明找到了待删除节点
                removekey(parent,cur);//这个方法就用来判断是哪种情况并且进行删除
                return true;
            }
        }
        return false;
        //上面循环走完说明cur为null了也没有找到待删除元素,就返回false
    }
    private void removekey(Node parent, Node cur) {
        //1.待删除元素的左子树为null的时候
        if(cur.left==null){
            if(cur==root){//说明要删除的节点就是根节点的时候
                root = cur.right;
            }else if(cur == parent.left){//待删除元素是它的父节点的左子树
                parent.left = cur.right;
            }else{//待删除节点是它的父节点的右子树
                parent.right = cur.right;
            }
        }else if(cur.right==null){//待删除元素的右子树为null的时候
            if(cur==root){//说明待删除节点就是根节点
                root = cur.left;
            }else if(cur == parent.left){//说明待删除节点是它的父节点的左子树
                parent.left=cur.left;
            }else{//说明待删除节点是它的父节点的右子树
                parent.right=cur.left;
            }
        }else{//说明待删除节点的左右子树都不为null的时候
            //先找到待删除元素的右子树中最小的那个
            //再把找到的这个最小值复制给待删除节点
            //然后删除这个最小值就可以了
            Node scapeGoat = cur.right;//用它来保存最小值节点,cur.right是一定存在的,如果不存在已经执行了上面
            Node goatparent = cur;//用它来保存最小值节点的父节点,以便删除
            while(scapeGoat.left!=null){
                goatparent = scapeGoat;//先让替罪羊的父节点暂时保存替罪羊节点的信息
                scapeGoat = scapeGoat.left;//让替罪羊一直往左走,因为最小值都是在左边
                //当左边为null的时候此时scape就不走了,scape此时就保留着最小值的信息。s
            }
            //循环结束时scape就指向了右子树中的最小值
            cur.key = scapeGoat.key;//将这个最小值复制到待删除节点
            //接下来删除替罪羊节点,替罪羊节点就一定没有左子树,只需要判断是替罪羊父亲的左子树还是右子树即可
            if(scapeGoat==goatparent.left){//这是左子树的情况
                goatparent.left=scapeGoat.right;
            }else{
                goatparent.right = scapeGoat.right;
            }
        }
    }

红黑树和AVL树我还没学。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值