二叉排序树

一、相关概念

  • 二叉排序树:BST: (Binary Sort(Search) Tree),对于二叉排序树的任何一个非叶子节点,要求左子节点的值比当前节点的值右子节点的值比当前节点的值

  • 示意图

在这里插入图片描述

二、基本应用

2.1 需求分析

给你一个数列 (7, 3, 10, 12, 5, 1, 9),要求能够高效的完成对数据的查询和添加,解决方案如下:

  • 使用未排序数组:直接在数组尾添加速度快(优点),但查找速度慢( 缺点)。
  • 使用排序数组:可以使用二分查找,查找速度快(优点),为了保证数组有序,在添加新数据时找到插入位置后,后面的数据需整体移动速度慢(缺点)。
  • 使用链式存储-链表:添加数据速度比数组快,不需要数据整体移动(优点),不管链表是否有序,查找速度都慢(缺点)。
  • 使用二叉排序树

2.2 代码示例

public class BinarySortTreeDemo {

    public static void main(String[] args) {

        int[] arr = {7, 3, 10, 12, 5, 1, 9, 2};
        // 创建二叉排序树。
        BinarySortTree binarySortTree = new BinarySortTree();
        // 循环的添加节点到二叉排序树。
        for (int i : arr) {
            binarySortTree.add(new Node(i));
        }

        // 中序遍历二叉排序树。
        System.out.println("中序遍历二叉排序树~");
        binarySortTree.infixOrder();
        // 1, 3, 5, 7, 9, 10, 12

        // 测试删除节点。
        for (int id : arr) {
            binarySortTree.del(id);
        }
        System.out.println("删除节点后:");
        binarySortTree.infixOrder();
        // binary tree is null.
    }
}

/**
 * 二叉排序树。
 */
class BinarySortTree {

    private Node root;

    public Node getRoot() {
        return root;
    }

    public void infixOrder() {
        if (null == root) {
            System.out.println("binary tree is null.");
        } else {
            this.root.infixOrder();
        }
    }

    public void add(Node node) {
        if (null == root) {
            this.root = node;
        } else {
            this.root.add(node);
        }
    }

    public Node searchParent(int id) {
        if (null == root) {
            return null;
        } else {
            return this.root.searchParent(id);
        }
    }

    public Node search(int id) {
        if (null == root) {
            return null;
        } else {
            return this.root.search(id);
        }
    }

    /**
     * 删除节点。
     *
     * @param id id
     */
    public void del(int id) {
        if (null == root) {
            throw new NullPointerException("binary tree is null.");
        } else {
            // [第一步]:找到需要删除的节点。
            Node target = search(id);

            // 没找到,则退出方法调用。
            if (null == target) {
                return;
            }
            // 若这颗二叉排序树只有一个节点,赋值 null 后就退出方法调用。
            boolean isOneNode = noChildNode(this.root);
            if (isOneNode) {
                this.root = null;
                return;
            }

            // [第二步]:找到待删除节点的父节点。
            Node parent = searchParent(id);
            boolean isLeafNode = noChildNode(target);
            boolean hasTwoTrees = hasTwoTrees(target);

            // [第三步]:根据不同情况执行对应的删除操作。
            // 情况一:删除叶子节点。
            if (isLeafNode) {
                delLeafNode(id, parent);
                // 情况二:删除有两颗子树的节点。
            } else if (hasTwoTrees) {
                int minId = delTreeMin(target.getRight());
                target.setId(minId);
                // 情况三:删除只有一颗子树的节点。
            } else {
                delJustOneTreeNode(parent, target, id);
            }
        }
    }

    /**
     * 删除只有一颗子树的节点。
     *
     * @param parent 父节点
     * @param target 待删除的目标节点
     * @param id     id
     */
    private void delJustOneTreeNode(Node parent, Node target, int id) {
        if (null != target.getLeft()) {
            delCurrentNode(parent, target.getLeft(), id);
        } else {
            delCurrentNode(parent, target.getRight(), id);
        }
    }

    /**
     * 删除当前节点。
     *
     * @param parent 父节点
     * @param node   待删除的目标节点
     * @param id     id
     */
    private void delCurrentNode(Node parent, Node node, int id) {
        if (parent != null) {
            // 如果目标节点是父节点的左子节点。
            if (parent.getLeft().getId() == id) {
                parent.setLeft(node);
            } else {
                // 目标节点是父节点的右子节点。
                parent.setRight(node);
            }
        } else {
            root = node;
        }
    }

    /**
     * 删除当前树的最小节点
     *
     * @param node 节点
     * @return int id
     */
    private int delTreeMin(Node node) {
        Node target = node;
        // 向左进行查找。
        while (null != target.getLeft()) {
            target = target.getLeft();
        }
        // 删除最小节点。
        del(target.getId());
        return target.getId();
    }

    /**
     * 判断当前节点是没有子节点的。
     *
     * @param node 节点
     * @return boolean
     */
    private boolean noChildNode(Node node) {
        return null == node.getLeft() && null == node.getRight();
    }

    /**
     * 判断当前节点是有两颗树的。
     *
     * @param node 节点
     * @return boolean
     */
    private boolean hasTwoTrees(Node node) {
        return null != node.getLeft() && null != node.getRight();
    }

    /**
     * 删除叶子节点。
     *
     * @param id     id
     * @param parent 父节点
     */
    private void delLeafNode(int id, Node parent) {
        //判断目标节点是父节点的左子节点,还是右子节点。
        if (null != parent.getLeft() && parent.getLeft().getId() == id) {
            parent.setLeft(null);
        } else if (null != parent.getRight() && parent.getRight().getId() == id) {
            parent.setRight(null);
        }
    }
}


/**
 * 节点。
 */
class Node {

    private int id;
    private Node left;
    private Node right;

    public Node(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Node getLeft() {
        return left;
    }

    public void setLeft(Node left) {
        this.left = left;
    }

    public Node getRight() {
        return right;
    }

    public void setRight(Node right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "Node id=[" + id + "]";
    }

    /**
     * 查找要删除的节点。
     *
     * @param id id
     * @return {@link Node}
     */
    public Node search(int id) {
        // 找到了。
        if (id == this.getId()) {
            return this;
            // 向左递归查找。
        } else if (id < this.getId()) {
            if (null == this.getLeft()) {
                return null;
            }
            return this.getLeft().search(id);
            // 否则,向右递归查找。
        } else {
            if (null == this.getRight()) {
                return null;
            }
            return this.getRight().search(id);
        }
    }

    /**
     * 查找要删除节点的父节点。
     *
     * @param id id
     * @return {@link Node}
     */
    public Node searchParent(int id) {
        if (
            // 如果当前节点就是要删除的节点的父节点,就进行返回。
                (null != this.getLeft() && this.getLeft().getId() == id) ||
                        (null != this.getRight() && this.getRight().getId() == id)
        ) {
            return this;
        } else {
            // 如果查找的值小于当前节点的值, 并且当前节点的左子节点不为空。
            if (null != this.getLeft() && id < this.getId()) {
                // 向左子树递归查找。
                return this.getLeft().searchParent(id);
            } else if (null != this.getRight() && id >= this.getId()) {
                // 向右子树递归查找。
                return this.getRight().searchParent(id);
            } else {
                // 没有找到父节点。
                return null;
            }
        }
    }

    /**
     * 添加节点。
     *
     * @param node 节点
     */
    public void add(Node node) {

        if (null == node) {
            throw new NullPointerException();
        }

        // 通过当前节点值判断与根节点的关系。
        if (this.getId() > node.getId()) {
            if (null == this.getLeft()) {
                // 左子节点赋值。
                this.setLeft(node);
            } else {
                // 左子树递归。
                this.getLeft().add(node);
            }
        } else {
            if (null == this.getRight()) {
                // 右子节点赋值。
                this.setRight(node);
            } else {
                // 右子树递归。
                this.getRight().add(node);
            }
        }
    }

    /**
     * 中序遍历。
     */
    public void infixOrder() {
        if (null != this.getLeft()) {
            this.getLeft().infixOrder();
        }
        System.out.println(this);
        if (null != this.getRight()) {
            this.getRight().infixOrder();
        }
    }
}

三、结束语


“-------怕什么真理无穷,进一寸有一寸的欢喜。”

微信公众号搜索:饺子泡牛奶

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值