zoukankan      html  css  js  c++  java
  • 二叉搜索树问题

    05_二叉搜索树

    1、二叉搜索树(Binary Search Tree)

    • 二叉搜索树是二叉树的一种,是应用非常广泛的一种二叉树,英文简称为BST
      • 又被称为:二叉查找树、二叉排序树
      • 任意一个节点的值都大于子树所有节点的值
      • 任意一个节点的值都小于子树所有节点的值
      • 它的左右子树也是一棵二叉搜索树
    • 二叉搜索树可以大大提高搜索数据的效率
    • 二叉搜索树存储的元素必须具备可比较性
      • 比如int、double等
      • 如果是自定义类型,需要指定比较方式
      • 不允许为null

    Snipaste_2021-03-19_13-38-50

    2、二叉搜索树的接口设计

    • int size() // 元素的数量
    • boolean isEmpty() // 是否为空
    • void clear() // 清空所有元素
    • void add(E element) // 添加元素
    • void remove(E element) // 删除元素
    • boolean contains(E element) // 是否包含某元素

    2.1、添加节点

    比如我们往下面这颗树中添加节点12,1

    Snipaste_2021-03-19_13-56-17

    我们的添加步骤是:

    • 找到父节点parent
    • 创建新节点node
    • parent.left = node 或者 parent.right = node

    假如我们要添加的元素在树中存在,我们建议覆盖旧的值

    public void add(E element){
        NodeNotNUllCheck(element);
    
        // 传入第一个节点
        if(root == null){
            root = new Node<>(element, null);
            size++;
            return;
        }
        Node<E> node = root;
        Node<E> parent = root;
        int cmp = 0;
        while(node != null){
            parent = node; // 父节点
            cmp = compareTo(node.element, element); // 方向
            if(cmp < 0){
                node = node.right;
            }else if(cmp > 0){
                node = node.left;
            }else{ // 相等,最好是覆盖掉
                node.element = element;
                return;
            }
        }
        Node<E> newNode = new Node<>(element, parent);
        if(cmp < 0){
            parent.right = newNode;
        }else{
            parent.left = newNode;
        }
        size++;
    
    }
    

    2.2、删除节点

    删除节点又分三种情况:

    删除叶子节点、度为1的节点、度为2的节点

    2.2.1、删除叶子节点

    对于删除叶子节点的这种情况,我们可以直接删除

    如下图所示,如果我们要删除叶子节点3,那么我们只需要让找到它的父节点2,然后执行node.parent.right = null即可直接删除,不过我们要保证的是node == node.parent.right

    同样的,假如我们要删除的是叶子节点10,那么我们也只需要找到它的父节点11,然后执行node.parent.left = null即可直接删除,不过我们要保证的是node == node.parent.left

    对于只含有一个根节点的树来说,也即node.parent == null,我们直接让root = null即可

    Snipaste_2021-03-20_13-52-03

    2.2.2、删除度为1的节点

    对于下图所示的情况来看,我们要删除度为1的节点,此时我们拿节点4和9来举例说明:

    我们把要删除的节点定义为node,它的孩子节点为child,我们从图上可以很清晰的看到,度为1的节点,它的孩子节点,要么左孩子要么是右孩子,也就是child 是 node.left 或者 child 是 node.right

    如果我们要删除的节点是它父亲节点的左子树的话,即我们图中的节点4,它是根节点7的左子树,那么我们删除它的时候,只要满足child.parent = node.parent 和 node.parent.left = child即可,也就是让2的父亲节点指向4的父亲节点,然后让7的左子节点为2,即可完成删除

    同样的如果我们要删除的节点是它父亲节点的右子树的话,我们只需要满足child.parent = node.parent 和 node.parent.right= child即可

    image-20210321103205501

    还有一种情况,就是如果node是根节点的话,如下图所示:

    我们只需要让root = child, child.parent = null即可完成

    image-20210321104112068

    2.2.3、删除度为2的节点

    如下图所示:

    ​ 假如我们要删除根节点5,那么我们只需要找到根节点的前驱结点,或者后继节点即可实现,前驱节点具体看https://blog.csdn.net/mj598_/article/details/115036091?spm=1001.2014.3001.5501

    image-20210321104320621

    最后代码如下:

    ​ 具体细节我已经写得很清楚了

    /**
    * 删除节点
    * @param element
    */
    public void remove(E element){
        remove(node(element));
    }
    
    private void remove(Node<E> node){
        if(root == null){
            return;
        }
    
        size--;
    
        // 如果度为2
        if(node.hasTowChildren()){
            // 找到它的前驱结点
            Node<E> pre = predecessor(node);
            node.element = pre.element;
            node = pre;
        }
    
         // 度为1的节点,它只存在左子节点,或者右子节点,如果左子节点不为空,那replacement就是node.left,反之,就是node.right
        Node<E> replacement = node.left != null ? node.left : node.right;
    
        // 如果replacement不为空的话,那么node肯定是度为1的节点
        if(replacement != null){   // node为度为1的节点
            replacement.parent = node.parent;
    
            if(node.parent == null){ //如果node的父节点为null,也就是node为根节点
                root = replacement;
            }else if (node == node.parent.left){  // 如果是左子节点的话,就让父节点的左子树指向自己的子节点
                    node.parent.left = replacement;
            }else if(node == node.parent.right){  // 如果是右子节点的话,就让父节点的左子树指向自己的子节点
                node.parent.right = replacement;
            }
        }else if(node.parent == null){   // 如果只有一个根节点的话,那么删除操作就是让它的根节点指向null即可
             root = null;
        }else {  // node是叶子节点
            if(node == node.parent.right){  // 如果这个叶子节点是父子节点的右子节点
                node.parent.right = null;   // 直接将它置为null
            }else {
                // 如果这个叶子节点是父子节点的左子节点
                // 直接将它置为null
                node.parent.left = null;
            }
        }
    }
    
    private Node<E> node(E element){
        Node<E> node = root;
        int cmp = compareTo(element, node.element);
        while (node != null){
            if(cmp == 0){
                return node;
            }else if (cmp < 0){
                node = node.left;
            }else {
                node = node.right;
            }
        }
    
        return null;
    }
    
  • 相关阅读:
    android内存知识普及抱怨墨迹内存大的同学看过来
    ICS 截屏(Screenshot)代码流程小结
    ICS Overlay主要流程
    Android中的sp和wp指针
    Android Display buffer_handle_t的定义
    adb shell input 命令
    百度地图API之MyLocationOverlay的使用(Android)
    由Android系统智能手机解锁图案引出的排列组合问题
    <script runat=server>、<%%>和<%#%>前台是服务器方法
    Oracle 日期运算 集合
  • 原文地址:https://www.cnblogs.com/coderD/p/14580747.html
Copyright © 2011-2022 走看看