第七章 二叉搜索树(BinarySearchTree)

第七章 二叉搜索树(BinarySearchTree)

推荐一些神奇的网站:

  • https://520it.com/binarytrees/
  • http://btv.melezinek.cz/binary-search-tree.html
  • https://www.cs.usfca.edu/~galles/visualization/BST.html

思考:在 n 个动态的整数中搜索某个整数?(查看其是否存在)

  • 假设使用动态数组存放元素,从第 0 个位置开始遍历搜索,平均时间复杂度: O ( n )

Untitled

  • 如果维护一个有序的动态数组,使用二分搜索,最坏时间复杂度: O ( logn )
    • 但是添加、删除的平均时间复杂度是: O ( n )

Untitled

  • 针对这个需求,有没有更好的解决方案?
    • 使用二叉搜索树,添加、删除、搜索的最坏时间复杂度均可优化至: O ( logn )

Untitled

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

注意:对于我们现在使用的二叉树来说,它的元素没有索引的概念

二叉搜索树的接口设计

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

添加元素: add( )

Untitled

/**
 * 添加元素
 *
 * @param element
 */
public void add(E element) {
    elementNotNullCheck(element);

    //根节点为空,相当于添加第一个节点
    if (root == null) {
        root = new Node<>(element, null);
        size++;
        return;
    }
    //添加的不是第一个节点
    //找到父节点
    Node<E> parent = root;
    Node<E> node = root;
    int cmp = 0;
    while (node != null) {
        cmp = commpare(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<>(element, parent); //创建新节点
    if (cmp > 0) {
        parent.right = newNode;
    } else {
        parent.left = newNode;
    }
    size++;
}

删除元素: remove( )

删除节点 – 叶子节点

叶子节点直接删除即可

Untitled

删除节点 – 度为1的节点

删除度为1的节点:用子节点替代原节点的位置

  • 如果要删除的节点不是根节点:如果要删除的节点不是根节点

Untitled

  • 如果要删除的节点是根节点

Untitled

删除节点 – 度为2的节点

Untitled

public void remove(E element) {
    remove(node(element));
}

private void remove(Node<E> node) {
    if (node == null) {
        return;
    }
    size--;
    if (node.hasTwoChildren()) { //度为2的节点
        //找到后继节点
        Node<E> s = successor(node);
        //用后继节点的值,覆盖度为2的节点的值
        node.element = s.element;
        //删除后继节点
        node = s;
    }
    //删除node节点(node的度必然是1或者0)
    Node<E> replacement = node.left != null ? node.left : node.right;

    if (replacement != null) { //node是度为1的节点
        //更改parent
        replacement.parent = node.parent;
        //更改parent的left,right的指向
        if (node.parent == null) { //node是度为1的节点并且是根节点
            root = replacement;
        } else {
            if (node == node.parent.left) {
                node.parent.left = replacement;
            } else { //node == node.parent.right
                node.parent.right = replacement;
            }
        }
    } else if (node.parent == null) { //node是叶子节点并且是根节点
        root = null;
    } else { //node是叶子节点,但不是根节点
        if (node == node.parent.left) {
            node.parent.left = null;
        } else { //node == node.parent.right
            node.parent.right = null;
        }
    }
}

private Node<E> node(E element) {
    Node<E> node = root;
    while (node != null) {
        int cmp = commpare(element, node.element);
        if (cmp == 0) {
            return node;
        } else if (cmp > 0) {
            node = node.right;
        } else { //cmp < 0
            node = node.left;
        }
    }
    return null;
}

BST完整代码

package cn.xx.java.tree;

import java.util.Comparator;
import java.util.LinkedList;
import java.util.Queue;

/**
 * 二叉搜索树
 *
 * @author xiexu
 * @create 2021-07-26 12:13 下午
 */
public class BST<E> extends BinaryTree<E> {
    private Comparator<E> comparator;

    public BST() {
        this(null);
    }

    public BST(Comparator<E> comparator) {
        this.comparator = comparator;
    }

    private void elementNotNullCheck(E element) {
        if (element == null) {
            throw new IllegalArgumentException("element must not be null");
        }
    }

    /**
     * 添加元素
     *
     * @param element
     */
    public void add(E element) {
        elementNotNullCheck(element);

        //根节点为空,相当于添加第一个节点
        if (root == null) {
            root = new Node<>(element, null);
            size++;
            return;
        }
        //添加的不是第一个节点
        //找到父节点
        Node<E> parent = root;
        Node<E> node = root;
        int cmp = 0;
        while (node != null) {
            cmp = commpare(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<>(element, parent); //创建新节点
        if (cmp > 0) {
            parent.right = newNode;
        } else {
            parent.left = newNode;
        }
        size++;
    }

    public void remove(E element) {
        remove(node(element));
    }

    private void remove(Node<E> node) {
        if (node == null) {
            return;
        }
        size--;
        if (node.hasTwoChildren()) { //度为2的节点
            //找到后继节点
            Node<E> s = successor(node);
            //用后继节点的值,覆盖度为2的节点的值
            node.element = s.element;
            //删除后继节点
            node = s;
        }
        //删除node节点(node的度必然是1或者0)
        Node<E> replacement = node.left != null ? node.left : node.right;

        if (replacement != null) { //node是度为1的节点
            //更改parent
            replacement.parent = node.parent;
            //更改parent的left,right的指向
            if (node.parent == null) { //node是度为1的节点并且是根节点
                root = replacement;
            } else {
                if (node == node.parent.left) {
                    node.parent.left = replacement;
                } else { //node == node.parent.right
                    node.parent.right = replacement;
                }
            }
        } else if (node.parent == null) { //node是叶子节点并且是根节点
            root = null;
        } else { //node是叶子节点,但不是根节点
            if (node == node.parent.left) {
                node.parent.left = null;
            } else { //node == node.parent.right
                node.parent.right = null;
            }
        }
    }

    private Node<E> node(E element) {
        Node<E> node = root;
        while (node != null) {
            int cmp = commpare(element, node.element);
            if (cmp == 0) {
                return node;
            } else if (cmp > 0) {
                node = node.right;
            } else { //cmp < 0
                node = node.left;
            }
        }
        return null;
    }

    /**
     * 判断元素是否存在
     *
     * @param element
     * @return
     */
    public boolean contains(E element) {
        return node(element) != null;
    }

    /**
     * @param e1
     * @param e2
     * @return 返回值等于0,代表e1和e2相等;返回值大于0,代表e1大于e2;返回值小于0,代表e1小于e2
     */
    private int commpare(E e1, E e2) {
        if (comparator != null) {
            return comparator.compare(e1, e2);
        } else {
            return ((Comparable<E>) e1).compareTo(e2);
        }
    }

    /**
     * 前序遍历
     */
    public void preorderTraversal() {
        preorderTraversal(root);
    }

    private void preorderTraversal(Node<E> node) {
        if (node == null) {
            return;
        }
        //先访问根节点
        System.out.println(node.element);
        //访问左子节点
        preorderTraversal(node.left);
        //访问右子节点
        preorderTraversal(node.right);
    }

    /**
     * 中序遍历
     */
    public void inorderTraversal() {
        inorderTraversal(root);
    }

    private void inorderTraversal(Node<E> node) {
        if (node == null) {
            return;
        }
        //先中序遍历左子树
        inorderTraversal(node.left);
        //输出根节点
        System.out.println(node.element);
        //再中序遍历右子树
        inorderTraversal(node.right);
    }

    /**
     * 后序遍历
     */
    public void postorderTraversal() {
        postorderTraversal(root);
    }

    private void postorderTraversal(Node<E> node) {
        if (node == null) {
            return;
        }
        //先遍历左子树
        postorderTraversal(node.left);
        //再遍历右子树
        postorderTraversal(node.right);
        //最后访问根节点
        System.out.println(node.element);
    }

    /**
     * 层序遍历
     */
    public void levelOrderTranversal() {
        if (root == null) {
            return;
        }
        Queue<Node<E>> queue = new LinkedList<>();
        //将头节点入队
        queue.offer(root);
        while (!queue.isEmpty()) {
            //将头结点出队
            Node<E> node = queue.poll();
            System.out.println(node.element);
            //如果左子节点不为空,就将左子节点入队
            if (node.left != null) {
                queue.offer(node.left);
            }
            //如果右子节点不为空,就将右子节点入队
            if (node.right != null) {
                queue.offer(node.right);
            }
        }
    }

}
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猿小羽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值