二叉搜索树(BST)

package com.zzk.data;

/**
 * 二叉搜索树(BST)
 */
public class BinarySearchTree<T extends Comparable> {

    Node root;

    public BinarySearchTree() {
        this.root = null;
    }

    static class Node<T extends Comparable> {
        public T key;
        public Node<T> left;
        public Node<T> right;
        public Node<T> parent;

        public Node(T key, Node left, Node right, Node parent) {
            this.key = key;
            this.left = left;
            this.right = right;
            this.parent = parent;
        }

        @Override
        public String toString() {
            return "Node{" +
                    "key=" + key +
                    '}';
        }
    }

    /**
     * 插入到树中
     */
    public void insert(T key) {
        if (null == root) {
            root = new Node(key, null, null, null);
        } else {
            insert(root, key);
        }
    }

    private void insert(Node node, T key) {
        Node<T> targetNode = new Node<>(key, null, null, node);
        if (key.compareTo(node.key) < 0) {
            if (null == node.left) {
                node.left = targetNode;
            } else {
                insert(node.left, key);
            }
        } else {
            if (null == node.right) {
                node.right = targetNode;
            } else {
                insert(node.right, key);
            }
        }
    }

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

    private void preOrder(Node node) {
        if (null == node) {
            return;
        }
        System.out.print(node.key);
        if (null != node.left) {
            preOrder(node.left);
        }
        if (null != node.right) {
            preOrder(node.right);
        }
    }

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

    private void inOrder(Node node) {
        if (null == node) {
            return;
        }
        if (null != node.left) {
            inOrder(node.left);
        }
        System.out.print(node.key);
        if (null != node.right) {
            inOrder(node.right);
        }
    }

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

    private void postOrder(Node node) {
        if (null == node) {
            return;
        }
        if (null != node.left) {
            postOrder(node.left);
        }
        if (null != node.right) {
            postOrder(node.right);
        }
        System.out.print(node.key);
    }

    /**
     * 查找
     */
    public Node<T> search(T key) {
        return search(root, key);
    }

    private Node<T> search(Node<T> node, T key) {
        //没找到
        if (null == node) {
            return null;
        }
        if (node.key.compareTo(key) == 0) {
            return node;
        }

        if (node.key.compareTo(key) > 0) {
            return search(node.left, key);
        } else {
            return search(node.right, key);
        }
    }

    /**
     * 非递归查找
     */
    public Node<T> search2(T key) {
        if (null == root) {
            return null;
        }

        Node cur = root;
        while (cur != null) {
            if (cur.key.compareTo(key) == 0) {
                return cur;
            } else if (cur.key.compareTo(key) > 0) {
                cur = cur.left;
            } else {
                cur = cur.right;
            }
        }
        return null;
    }

    /**
     * 获取最大的节点,主要为了删除节点功能中 可以获取 前驱节点和后驱节点做准备
     * (目标节点的 前驱节点 : 是左子树的最大节点, 也是中序遍历中 "目标节点"的 前一个节点)
     * (目标节点的 后驱节点 : 是右子树的最小节点, 也是中序遍历中 "目标节点"的 后一个节点)
     */
    public Node maxNode() {
        return maxNode(root);
    }

    private Node maxNode(Node node) {
        if (node == null) {
            return null;
        }

        Node cur = node;
        while (cur != null) {
            if (cur.right != null) {
                cur = cur.right;
            } else {
                return cur;
            }
        }
        return null;
    }

    public Node minNode() {
        return minNode(root);
    }

    private Node minNode(Node node) {
        if (node == null) {
            return null;
        }

        Node cur = node;
        while (cur != null) {
            if (cur.left != null) {
                cur = cur.left;
            } else {
                return cur;
            }
        }
        return null;
    }

    /**
     * 查找一个节点的前驱节点:
     * 1. 如果查找节点有左子树,那么他的前驱节点就是左子树的最大节点
     * 2. 如果查找节点没有左子树,那么分两种情况
     *          a: 查找节点是右节点,那么查找节点的父节点就是前驱节点
     *          b: 查找节点是左节点,那么顺着查找节点往上查,  如果 "被查节点" 是 "被查节点父节点" 的 右孩子,那么 "被查节点的父节点" 就是前驱
     */
    private Node pre(Node node) {
        if (node.left != null) {
            return maxNode(node.left);
        } else {
            if (node.parent == null) {
                return null;
            }

            if (node.parent.right == node) {
                return node.parent;
            } else {
                Node p = node.parent;
                while (p != null && p.left == node) {
                    node = p;
                    p = p.parent;
                }
                return p;
            }
        }
    }

    /**
     * 查找一个几点的后驱节点:
     * 1. 如果查找节点有右子树,那么他的前驱节点就是右子树的最小节点
     * 2. 如果查找节点没有右子树,那么分两种情况
     *          a: 查找节点是左节点,那么查找节点的父节点就是后驱节点
     *          b: 查找节点是右节点,那么顺着查找节点往上查,  如果 "被查节点" 是 "被查节点父节点" 的 左孩子,那么 "被查节点的父节点" 就是后驱
     */
    private Node after(Node node) {
        if (node.right != null) {
            return minNode(node.right);
        } else {
            if (node.parent == null) {
                return null;
            }

            if (node.parent.left == node) {
                return node.parent;
            } else {
                Node p = node.parent;
                while (p != null && p.right == node) {
                    node = p;
                    p = p.parent;
                }
                return p;
            }
        }
    }


    /**
     * 删除节点 (不考虑根节点的删除)
     *
     * 删除代码未优化,只根据规则进行对应实现.
     *
     * 1. 被删除节点是叶子节点,  直接将该节点的父节点的 "左节点或右节点" 置为null
     * 2. 被删除节点只有左子树,或者只有右子树,那么将孙节点直接交给祖父节点, 然后将被删除节点置为null
     * 3. 被删除节点既有左子树, 又有右子树, 那么选择将左子树的 "前驱节点" 的值 赋值给待删除节点, 然后 根据第2点,删除节点即可
     *
     * 具体的规则可参考 https://www.bilibili.com/video/BV1sv411j7hZ
     */
    public void remove(T key) {
        Node node = remove(root, key);
        if (node != null) {
            node = null;
        }
    }

    private Node remove(Node node, T key) {

        Node<T> deleteTargetNode = search2(key);
        //不存在要删除的节点
        if (null == deleteTargetNode) {
            return null;
        }

        //是叶子节点
        if (deleteTargetNode.left == null && deleteTargetNode.right == null) {
            if (deleteTargetNode.parent.left == deleteTargetNode) {
                deleteTargetNode.parent.left = null;
            }
            if (deleteTargetNode.parent.right == deleteTargetNode) {
                deleteTargetNode.parent.right = null;
            }
            return deleteTargetNode;
        }

        //要被刪除的节点只有左子树
        if (deleteTargetNode.left != null && deleteTargetNode.right == null) {
            if (deleteTargetNode.parent.left == deleteTargetNode) {
                deleteTargetNode.parent.left = deleteTargetNode.left;
            }
            if (deleteTargetNode.parent.right == deleteTargetNode) {
                deleteTargetNode.parent.right = deleteTargetNode.left;
            }
            return deleteTargetNode;
        }

        //要被刪除的节点只有右子树
        if (deleteTargetNode.left == null && deleteTargetNode.right != null) {
            if (deleteTargetNode.parent.left == deleteTargetNode) {
                deleteTargetNode.parent.left = deleteTargetNode.right;
            }
            if (deleteTargetNode.parent.right == deleteTargetNode) {
                deleteTargetNode.parent.right = deleteTargetNode.right;
            }
            return deleteTargetNode;
        }

        //要被刪除的节点有左子树和右子树
        if (deleteTargetNode.left != null && deleteTargetNode.right != null) {
            //找到左子树的前驱节点
            Node pre = pre(deleteTargetNode);
            //前驱节点赋值 给 要删除节点
            deleteTargetNode.key = (T) pre.key;

            //前驱节点肯定没有右子树,所以只判断是否有左子树即可 (因为中序遍历 左中右的顺序, 要是有右节点, 那么右节点肯定要比当前选中的前驱节点大,不可能)

            //前驱节点没有左子树
            if (pre.left == null) {
                pre.parent = null;
                return pre;
            }else{
                //前驱节点有左子树
                if (pre.parent.left == pre) {
                    pre.parent.left = pre.left;
                }
                if (pre.parent.right == pre) {
                    pre.parent.right = pre.left;
                }
                return deleteTargetNode;
            }
        }
        return null;
    }


    public static void main(String[] args) {

        BinarySearchTree<Integer> tree = new BinarySearchTree<>();
        tree.insert(1);
        tree.insert(5);
        tree.insert(4);
        tree.insert(3);
        tree.insert(2);
        tree.insert(6);

//        System.out.println(tree.search(1));
//        System.out.println(tree.search2(1));
//        tree.preOrder();
//        System.out.println();
//        tree.inOrder();
//        System.out.println();
//        tree.postOrder();

//        System.out.println(tree.maxNode());
//        System.out.println(tree.minNode());

//        Node search1 = tree.search(1);
//        Node search2 = tree.search(7);
//        Node search3 = tree.search(4);
//        Node search4 = tree.search(3);
//        Node search5 = tree.search(8);
//        Node search6 = tree.search(2);
//        System.out.println(tree.pre(search1));
//        System.out.println(tree.after(search1));
//        System.out.println(" ");
//        System.out.println(tree.pre(search2));
//        System.out.println(tree.after(search2));
//        System.out.println(" ");
//        System.out.println(tree.pre(search3));
//        System.out.println(tree.after(search3));
//        System.out.println(" ");
//        System.out.println(tree.pre(search4));
//        System.out.println(tree.after(search4));
//        System.out.println(" ");
//        System.out.println(tree.pre(search5));
//        System.out.println(tree.after(search5));
//        System.out.println(" ");
//        System.out.println(tree.pre(search6));
//        System.out.println(tree.after(search6));
//        System.out.println(" ");

        tree.inOrder();
        System.out.println("");
        tree.remove(2);
        tree.inOrder();

    }


}

  • 代码中的删除节点功能,并没有考虑根节点的删除
  • 通过BST的代码实现,强化了二叉搜索树的基本原理,复习了树的基本概念, 其中较为困难的是节点的删除功能, 对于理论知识的遗忘和欠缺,导致我必须在视频教程的基础上,重学BST的删除, 对于删除节点的代码,只按照规则进行实现, 没有重构,看起来有点别扭…
  • 该视频对于节点的删除,原理讲的还是很好,代码讲解有点绕,可以参考.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值