二叉查找树(BST)

二叉查找树

二叉查找树,也称为二叉排序树二叉搜索树
二叉查找树结合了链表插入的灵活性有序数组查找(二分查找)的高效性
用二叉查找树实现有序符号表的API。

public class BST<Key extends Comparable<Key>, Value> {

    private Node root; //根结点

    //链表结点类
    private class Node {
        private Key key; //键
        private Value val; //值
        private Node left, right; //左右孩子
        private int N; //以该结点为根的子树中的结点总数
        public Node(Key key, Value val, int N) {
            this.key = key;
            this.val = val;
            this.N = N;
        }
    }

    //有序符号表的相关方法
    //...
    //...
}
  • size()方法
    返回以该结点为根的子树中结点总数。
    public int size() {
        return size(root);
    }

    private int size(Node x) {
        if(x == null) return 0;
        else return x.N; //直接返回该结点的N
    }
  • get()方法
    查找键对应的值
    public Value get(Key key) {
        return get(root, key);
    }
    /** 
     * 查找操作
     * 在以x为根的子树中查找键对应的值。
     * 这里采用递归方式。
     */
    private Value get(Node x, Key key) {
        if(x == null) //未找到key,返回null
            return null;
        int cmp = key.compareTo(x.key);
        if      (cmp < 0) //key < x.key
            return get(x.left, key); //在左子树中查找
        else if (cmp > 0) //key > x.key
            return get(x.right, key); //在右子树中查找
        else //key = x.key
            return x.val; //返回该键对应的值
    }
  • put()方法
    插入键值对
    public void put(Key key, Value val) {
        root = put(root, key, val);
    }

    /**
     * 插入操作
     * 如果key存在于以x为根的子树中,更新它的值。
     * 如果key不存在,插入该键值对。
     * 每查找到一个结点都要更新它的规模信息和左(右)链接信息。 
     * 这里采用递归方式。
     */
    private Node put(Node x, Key key, Value val) {
        if(x == null) //如果不存在key
            return new Node(key ,val,1); //返回新结点
        int cmp = key.compareTo(x.key);
        if      (cmp < 0) //key < x.key
            x.left = put(x.left, key, val); //在左子树查找,并将该结点左链接置为返回的结点
        else if (cmp > 0) //key > x.key
            x.right = put(x.right, key, val); //不这样赋值的话当前结点就无法指向新插入的孩子结点(如果它的孩子结点是待插入结点)
        else //key = x.key
            x.key = key;
        x.N = size(x.left) + size(x.right) + 1; //更新当前结点的N
        return x;
    }
  • select()方法
    查找并返回排名为k的键

    /**
     * 查找并返回排名为k的键(即树中正好有k个小于它的键)
     */
    public Key select(int k) {
        return select(root, k).key;
    }

    private Node select(Node x, int k) {
        if      (x == null)
            return null;
        int t = size(x.left);
        if      (t > k) //排名为k的结点位于左子树中
            return select(x.left, k);
        else if (t < k) //排名为k的结点位于右子树中
            return select(x.right, k-t-1);
            //排名k减去左子树规模和当前结点
        else //当前结点排名为k
            return x;
    }
  • rank()方法
    查找并返回键的排名
    /**
     * 查找并返回键为key的排名
     */
    public int rank(Key key) {
        return rank(root, key);
    }

    private int rank(Node x, Key key) {
        if      (x == null)
            return 0;
        int cmp = key.compareTo(x.key);
        if      (cmp < 0) //位于左子树中
            return rank(x.left, key); 
        else if (cmp > 0) //位于右子树中
            return size(x.left) + rank(x.right, key) + 1;
            //要加上左子树结点数和当前结点
        else 
            return size(x.left);
    }
  • min()方法
    返回最小的键
    返回最大的键max()方法类似
    /**
     * 返回最小的键
     */
    public Key min() {
        return min(root).key;
    }

    private Node min(Node x) {
        if(x.left == null)
            return x;
        return min(x.left);
    }
  • deleteMin()方法
    删除最小结点
    删除最大结点deleteMax()方法类似
    /**
     * 删除最小节点
     */
    public void deleteMin() {
        root = deleteMin(root);
    }

    private Node deleteMin(Node x) {
        if (x.left == null)  //x结点是最小结点
            return x.right; 
            //返回当前结点右结点(x的父结点左链接将指向x.right)
            //x将被gc回收
        x.left = deleteMin(x.left);
        x.N = size(x.left) + size(x.right) + 1;
        //更新结点规模
        return x;
    }
  • delete()方法
    删除一个结点可以将当前结点x替换为右子树中最小的结点。
    具体删除一个结点的步骤:
  • 1、将即将删除的结点的链接保存为t
  • 2、x指向它的后继结点min(t.right)
  • 3、x的右链接指向deleteMin(t.right) (详见deleteMin()方法)
  • 4、x的左链接指向t.left
public void delete(Key key) {
        root = delete(root, key);
    }

    private Node delete(Node x, Key key) {
        if(x == null)
            return null;
        int cmp = key.compareTo(x.key);
        if      (cmp < 0)
            return x.left = delete(x.left, key);
        else if (cmp > 0)
            return x.right = delete(x.right, key);
        else { //当前结点是被删除结点
            if (x.right == null) //右子树为空
                return x.left;
            if (x.left == null) //左子树为空
                return x.right;
            Node t = x; //步骤1
            x = min(t.right); //步骤2
            x.right = deleteMin(t.right); //步骤3
            x.left = t.left; //步骤4
        }
        x.N = size(x.left) + size(x.right) + 1;
        //更新当前结点的规模
        return x;
    }
  • keys()方法
    用到了队列
    /**
     * 二叉查找树的范围查找
     */
    public Iterable<Key> keys() {
        return keys(min(), max());
    }

    public Iterable<Key> keys(Key lo, Key hi) {
        Queue<Key> queue = new LinkedList<Key>();
        keys(root, queue, lo, hi);
        return queue;
    }

    private void keys(Node x, Queue<Key> queue, Key lo, Key hi) {
        if (x == null)
            return;
        int cmplo = lo.compareTo(x.key);
        int cmphi = hi.compareTo(x.key);
        if (cmplo < 0)
            keys(x.left, queue, lo, hi);
        if (cmplo <= 0 && cmphi >=0)
            queue.add(x.key);
        if (cmphi > 0)
            keys(x.right, queue, lo, hi);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值