二叉搜索树问题

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;
}
posted @ 2021-03-26 10:07  codeFiler  阅读(62)  评论(0编辑  收藏  举报