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

    二叉搜索树(Binary Search Tree)

    定义:二叉搜索树是二叉树的一种,是应用非常广泛的一种二叉树,英文简称为 BST
    性质:

    • 任何一个节点的值都大于其左子树所有节点的值
    • 任意一个节点的值都小于其右子树所有节点的值
    • 它的左右子树也是一棵二叉搜索树
    • 二叉搜索树可以大大提高搜索数据的效率
    • 二叉搜索树存储的元素必须具备可比较性
    • 比如 int、double 等
    • 如果是自定义类型,需要指定比较方式
    • 不允许为 null

    二叉搜索树的接口设计

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

    接口的实现

    添加节点

    步骤:

    1. 找到父节点parent
    2. 创建新节点
    3. parent.left = node || parent.right = node
    4. 如果是相同值,覆盖旧的值

    实现代码:

    //添加节点
    public void add(E element){
        elementNotNullCheck(element);
        //添加第一个节点
        if (root == null){
            root = new Node<E>(element, null);
            size++;
            return;
        }
    
        //添加非第一个节点
        //找到父节点
        Node<E> parent = root;
        Node<E> node = root;
        int cmp = 0;
        while(node != null){
            cmp = compare(element, node.element);
            parent = node;//保存新添加节点的父节点是谁
            if(cmp > 0){
                node = node.right;
            } else if (cmp < 0){
                node = node.left;
            } else { //相等
                node.element = element;//新元素覆盖老元素
                return;
            }
        }
    
        //添加到父节点的哪个位置
        Node<E> newNode = new Node<E>(element, parent);
        if(cmp > 0){
            parent.right = newNode;
        } else {
            parent.left = newNode;
        }
        size++;
    }
    
    元素之间的比较设计
    1. 允许外界传入一个Comparator自定义比较方案
    2. 如果没有传入Comparator,强制认为元素已经实现了Compareable接口
      代码:
    //comparator字段
    private Comparator<E> comparator;
    //二叉搜索树的构造器
    public BinarySearchTree(){
        this(null);
    }
    public BinarySearchTree(Comparator<E> comparator){
        this.comparator = comparator;
    }
    
    /**
     * 节点值比较方法
     * 返回值等于0,e1 == e2;
     * 返回值大于0,e1大于e2;
     * 返回值小于0,e1小于e2.
     */
    private int compare(E e1, E e2){
        if (comparator != null){
            return comparator.compare(e1,e2);
        }
        return ((Comparable<E>)e1).compareTo(e2);
    }
    
    根据元素内容获取节点
    //获取元素对应的节点
    private Node<E> node(E element){
        Node<E> node = root;
        while (node != null){
            int cmp = compare(element, node.element);
            if (cmp == 0) return node;
            if (cmp > 0) {
                node = node.right;
            } else {
                node = node.left;
            }
        }
        return null;
    }
    

    删除节点

    情况有四种:

    1. remove节点是叶子节点

    直接删除
    若 node = node.parent.left;
    则 node.parent.left = null;
    若 node = node.parent.right;
    则 node.parent.right = null;
    若 node.parent = null;
    则 node.parent.left = null;

    1. remove节点是度为1的节点

    用子节点替代原节点的位置
    设child是node.left或者child是node.right
    用child替代node的位置

    • 如果node是左子节点
      则 child.parent = node.parent
      node.parent.left = child
    • 如果node是右子节点
      则 child.parent = node.parent
      node.parent.right = child
    • 如果node是根节点
      则 root = child
      child.parent = null
    1. remove节点是度为2的节点

    用前驱或者后继节点的值覆盖原节点的值
    然后删除相应的前驱或者后继节点
    注意:如果一个节点的度为2,那它的前驱、后继节点的度只可能是1 || 0

    1. remove节点是根节点
    //移除节点
    public void remove(E element){
        remove(node(element));
    }
    
    private void remove(Node<E> node){
        if (node == null) return;
        size --;
    
        //删除度为2的节点
        if (node.hasTwoChildren()){
            //找到node的后继节点
            Node<E> s = successor(node);
            //将后继节点的值赋值给node节点
            node.element = s.element;
            //删除后继节点
            node = s;
        }
    
        //删除度为1或者为0的节点
        Node<E> replacement = node.left != null ? node.left : node.right;
    
        if (replacement != null) {//node是度为1的节点
            //更改parent
            replacement.parent = node.parent;
            //更改node的left||right指向
            if (node.parent == null) {//node是度为1的节点并且是根节点
                root = replacement;
            } else if (node == node.parent.right){//node是父节点的右节点
                node.parent.right = replacement;
            } else {//node是父节点的左节点
                node.parent.left = replacement;
            }
        } else if (node.parent == null) { //node是叶子节点并且是根节点
            root = null;
        } else { //node是叶子节点但不是根节点
            if (node == node.parent.right){
                node.parent.right = null;
            } else {
                node.parent.left = null;
            }
        }
    }
    
  • 相关阅读:
    [软件工程 2018西北师范大学]实验一 软件工程准备 评分
    【集美大学1411_助教博客】助教总结
    【集美大学1411_助教博客】团队作业10——项目复审与事后分析(Beta版本)
    【集美大学1411_助教博客】团队作业9——测试与发布(Beta版本)
    【集美大学1411_助教博客】团队作业8——第二次项目冲刺(Beta阶段)
    【集美大学1411_助教博客】alpha阶段后 成绩
    Javascript 异步加载详解
    复选框,全选或者全不选
    Java六大必须理解的问题
    写了一个Java的简单缓存模型
  • 原文地址:https://www.cnblogs.com/Java0120/p/12676831.html
Copyright © 2011-2022 走看看