高效实现二叉搜索树(java)

BST 实现

package src.dataStructure;

/**
 * BST二叉搜索树,查找效率为1.39lgN
 * put()方法的效率仅仅比查找多了一次比较而已
 * 增删改查的效率都为对数阶
 * 树的最坏情况下的效率与树度高度成正比
 * BST的良好性能依赖于键的随机分布性,如果键分布不随机甚至是顺序或者逆序
 * 则BST的性能会达到线性阶。但可以使用AVL平衡二叉树保证无论键的分布是否随机化
 * 操作的效率都是对数阶
 * 方法:size()、put()、get()、max()、min()、floor()、ceiling()、select()、rank()、
 * deleteMin()、delete()、Keys()
 * @param <Key>
 * @param <Value>
 */
public class BST<Key extends Comparable<Key>,Value>
{

    private class Node
    {
        private Key key;
        private Value value;
        private Node left;
        private Node right;
        private int N;

        public Node(Key key, Value value,int N)
        {
            this.key = key;
            this.value = value;
            this.N = N;
        }

        @Override
        public String toString()
        {
            return "[key:" + key + ",value:" + value + "]";
        }
    }

    private Node root;

    /**
     * 返回根节点的结点总数
     * @return
     */
    public int size()
    {
        return size(root);
    }

    /**
     * 返回以root为根节点的二叉树的节点总数
     * @param node
     * @return
     */
    public int size(Node node)
    {
        if (node == null)
            return 0;
        else
            return node.N;
    }

    public Value get(Key key)
    {
        return get(root,key);
    }

    /**
     * 在以node为根节点的二叉树中,根据key查找结点,找到Value,否则返回null
     * 比较key与根节点key的值
     * 如果key小于根节点,则递归根节点左子树继续查找
     * 如果key等于根节点,则根节点就是要查找的结点,返回value
     * 如果key大于根节点,则递归右子树继续查找
     * @param node
     * @param key
     * @return
     */
    private Value get(Node node,Key key)
    {
        if (node == null)
            return null;
        int com = key.compareTo(node.key);
        if (com == 0)
            return node.value;
        else if (com < 0)
            return get(node.left,key);
        else
            return get(node.right,key);
    }

    public void put(Key key,Value value)
    {
       if (this.root == null)
           this.root = new Node(key,value,1);
       else
           put(root,key,value);
    }

    /**
     * 将新的键值对插入BST中,如果键已经存在,则更新value的值,否则将
     * 新的键值对插入树中
     *操作与get()方法大相径庭,在二叉树中查找key,如果找到更新value,如果
     * 没有找到,则当前路径一定遍历到了叶子结点的链接,它是个空链接,且
     * 该位置就是要插入结点的位置,则创建一个新的结点,并把叶子结点的链接赋值给它,
     * 同时因为创建了一个结点,则遍历路径上的所有结点的结点计数器N+1
     *
     * @param node
     * @param key
     * @param value
     */
    public Node put(Node node,Key key,Value value)
    {
        if (node == null)
            return new Node(key,value,1);

        int com = key.compareTo(node.key);
        if (com == 0)
            node.value = value;
        else if (com < 0)
            node.left = put(node.left,key,value);
        else
            node.right = put(node.right,key,value);

        node.N = size(node.left) + size(node.right) + 1;
        return node;
    }


    /**
     * 当二叉树为空时,注意空指针异常
     * @return
     */
    public Key min()
    {
        Node res = min(root);
        if (res != null)
            return res.key;
        else
            return null;
    }

    /**
     * 返回以node为根节点的二叉树的最小键的结点
     * 如果根节点的左子树不为空,则最小键一定在左子树中,递归左子树继续查找
     * 如果根节点的左子树为空,则最小键就是根节点,返回根节点
     * @param node
     * @return
     */
    private Node min(Node node)
    {
        if (node.left != null)
            return min(node.left);
        else
            return node;
    }

    /**
     * 当二叉树为空时,注意空指针异常
     * @return
     */
    public Key max()
    {
        Node res = max(root);
        if (res != null)
            return res.key;
        else
            return null;
    }

    /**
     * 返回以node为根节点的二叉树中的最大键的结点
     * 如果根节点的右子树不为空,则最大键一定在右子树中,则递归右子树进行查找
     * 如果根节点的右子树为空,则根节点就是最大键,返回根节点
     * @param node
     * @return
     */
    private Node max(Node node)
    {
        if (node.right != null)
            return max(node.right);
        else
            return node;
    }


    public Key floor(Key key)
    {
        Node res = floor(root,key);
        if (res != null)
            return res.key;
        else
            return null;
    }

    /**
     * 返回以node为根节点的二叉树中,小于等于key的最大键
     * 将key与根节点比较
     * 如果key等于根节点,则小于等于key的最大键就是根节点
     * 如果key小于根节点,则小于等于key的最大键一定在左子树中,递归左子树进行查找
     * 如果key大于根节点,则当右子树中存在小于等于key的结点时,小于等于key的最大键才会在右子树中
     * 否则小于等于key的最大键就是根节点,返回根节点
     * @param node
     * @return
     */
    private Node floor(Node node,Key key)
    {
        if (node == null)
            return null;
        int com = key.compareTo(node.key);
        if (com == 0)
            return node;
        else if (com < 0)
            return  floor(node.left,key);
        else
        {
            Node res = floor(node.right,key);
            if (res != null)
                return res;
            else
                return node;
        }
    }


    public Key ceiling(Key key)
    {
        Node res = ceiling(root,key);
        if (res != null)
            return res.key;
        else
            return null;
    }

    /**
     * 返回以node为根节点的二叉树中,大于等于key的最小键
     * 比较key与root.key的大小
     * 如果key等于根节点,则大于等于key的最小键就是root
     * 如果key大于根节点,则大于等于key的最小键一定在右子树中,则递归右子树继续查找
     * 如果key小于根节点,则只有当左子树中存在大于等于key的结点时,大于等于key的最小键才会在左子树中
     * 否则大于等于key的最小键就是根节点
     * @param node
     * @param key
     * @return
     */
    private Node ceiling(Node node,Key key)
    {
        if (node == null)
            return null;
        int com = key.compareTo(node.key);
        if (com == 0)
            return node;
        else if (com > 0)
            return ceiling(node.right,key);
        else
        {
            Node res = ceiling(node.left,key);
            if (res != null)
                return res;
            else
                return node;
        }
    }

    public Key select(int k)
    {
        Node res = select(root,k);
        if (res != null)
            return res.key;
        else
            return null;
    }

    /**
     * 返回以node为根节点的二叉树中,排名为k的结点(即有k个结点的键小于它)
     * 判断根节点左子树结点个数size,如果左子树结点个数等于k,则根节点就是排名为k的结点,返回根节点
     * 如果k小于size,则排名为k的结点一定在左子树中,递归左子树继续查找
     * 如果k>size,则排名为k的结点一定在右子树中,递归右子树查找排名为(k-size-1)的结点
     * @param node
     * @param k
     * @return
     */
    private Node select(Node node,int k)
    {
        if (node == null)
            return null;
        int size = size(node.left);
        if (k < size)
            return select(node.left,k);
        else if (k == size)
            return node;
        else
            return select(node.right,k - size - 1);
    }


    public int rank(Key key)
    {
        return rank(root,key);
    }

    /**
     * 返回以node为根节点的二叉树,键key的结点的排名
     * 比较key与根节点的 大小
     * 如果key等于根节点,则排名为左子树中结点的数量
     * 如果key小于根节点,则递归左子树进行查找
     * 如果key大于根节点,则排名为左子树中结点的数量+1+在右子树中的排名
     * @param node
     * @param key
     * @return
     */
    private int rank(Node node,Key key)
    {
       if (node == null)
           return 0;
       int com = key.compareTo(key);
       if (com < 0)
           return rank(node.left,key);
       else if (com == 0)
           return size(node.left);
       else
           return size(node.left) + 1 + rank(node.right,key);
    }

    public void deleteMin()
    {
        root = deleteMin(root);
    }

    /**
     * 删除以node为根节点的二叉树中键最小的结点
     * 找到键最小的结点,返回它的右子树,并让最小结点的父节点的左链接指向
     * 它的右子树
     * 当没有链接指向被删除的结点时,被删除的结点会被JVM当作垃圾回收
     * @param node
     * @return
     */
    private Node deleteMin(Node node)
    {
        if (node.left == null)
            //当前结点就是键最小的结点,返回它的右子树
            return node.right;
        node.left = deleteMin(node.left);
        node.N = size(node.left) + size(node.right) + 1;
        return node;
    }


    public void delete(Key key)
    {
        root = delete(root,key);
    }
    /**
     * 通过key查找要删除的结点,将该节点右子树中键最小的结点替代该位置
     * 1.找到要删除的节点后,将结点赋值给引用遍历t
     * 2.找到该节点右子树中键最小的结点,并将该节点的引用赋值给x
     * 3.调用deleteMin(t.right)删除右子树中最小的结点,方法返回删除后右子树的根节点
     * 4.将x替代要删除的结点,即将x左链接指向t的左链接,右链接指向deleteMin(t.right)
     * @param node
     * @param key
     * @return
     */
    private Node delete(Node node,Key key)
    {
        if (node == null)
            //要删除的结点不存在
            return null;
        int com = key.compareTo(node.key);
        if (com < 0)
            node.left = delete(node.left,key);
        else if (com == 0)
        {
            //1.要删除的结点是叶子节点或者只有一个子结点
            //则将该子节点赋值返回,即赋值给它父节点的链接
            if (node.left == null)
                return node.right;
            if (node.right == null)
                return node.left;

            //2.要删除的结点有两个子结点
            Node t = node; //该节点就是要删除的结点
           node = min(t.right);
           node.left = t.left;
           node.right = deleteMin(t.right);
        }
        else
            node.right = delete(node.right,key);

        node.N = size(node.left) + size(node.right) + 1;
        return node;

    }


    public void preOrder()
    {
        if (root != null)
            preOrder(root);
        else
            System.out.println("二叉树为空");
    }

    public void preOrder(Node node)
    {
        if (node == null)
            return;
        preOrder(node.left);
        System.out.println(node);
        preOrder(node.right);
    }
}

测试:

 BST<String, String> BST = new BST<>();
        BST.put("N","南京");
        BST.put("B","北京");
        BST.put("S","上海");
        BST.put("G","广州");
        BST.put("H","杭州");
        BST.put("C","重庆");
        BST.put("X","厦门");
        BST.put("L","拉萨");


        System.out.println(BST.get("L"));
        System.out.println("****************分界线********************");

        BST.preOrder();
        System.out.println("****************分界线********************");

        System.out.println(BST.max());
        System.out.println(BST.min());
        System.out.println(BST.floor("D"));
        System.out.println(BST.ceiling("R"));
        System.out.println("****************分界线********************");

        System.out.println(BST.size());
        System.out.println(BST.select(3));
        System.out.println(BST.rank("N"));
        System.out.println("****************分界线*********************");

        BST.deleteMin();
        BST.delete("S");
        BST.preOrder();
        System.out.println("****************分界线********************8");

输出:

拉萨
分界线****
[key:B,value:北京]
[key:C,value:重庆]
[key:G,value:广州]
[key:H,value:杭州]
[key:L,value:拉萨]
[key:N,value:南京]
[key:S,value:上海]
[key:X,value:厦门]
分界线****
X
B
C
S
分界线****
8
H
5
分界线*****
[key:C,value:重庆]
[key:G,value:广州]
[key:H,value:杭州]
[key:L,value:拉萨]
[key:N,value:南京]
[key:X,value:厦门]
****************分界线********************8

参考:

《Algorithms Fourth Edition》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值