第三章 查找(二) - 《算法》读书笔记

第三章 查找(二)

3.2 二叉查找树

一颗二叉查找树(BST)是一颗二叉树,其中每个节点都含有一个Comparable的键(以及相关联的值)且每个结点的值都大于其左子树中的任意节点的键,而小于右子树的任意结点的键。

3.2.1 基本实现

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;
        }
    }
    
    public int size(){
        return size(root);
    }
    
    private int size(Node x){
        if (x == null) return 0;
        else return x.N;
    }
    
    public Value get(Key key){
        return get(root, key);
    }
    
    private Value get(Node x, Key key){
        if (x == null) return null;
        int cmp = key.compareTo(x.key);
        if (cmp < 0) return get(x.left, key);
        else if (cmp > 0) return get(x.right, key);
        else return x.val;
    }
    
    public void put(Key key, Value val){
		root = put(root, key.val);
    }
    
    private Node put(Node x, Key key, Value val){
        if (x == null) return new Node(key, val, 1);
        int cmp = key.compareTo(x.key);
        if (cmp < 0) x.left = put(x.left, key, val);
        else if (cmp > 0) x.right = put(x.right, key, val);
        else x.val = val;
        x.N = size(x.left) + size(x.right) + 1;
        return x;
    }
}

3.2.2 分析

在由N个随机键构造的二叉查找树中,查找命中平均所需的比较次数为~2lnN(约1.39lgN)。

在由N个随机键构造的二叉查找树中,插入操作和查找未命中平均所需的比较次数为~2lnN(约1.39lgN)。

3.2.3 有序性相关的方法与删除操作

3.2.3.2 向上取整和向下取整

  • 如果给定的键key小于二叉查找树的根结点的键,那么小于等于key的最大键floor(key)一定在根结点的左子树中;如果给定的键key大于二叉查找树的根结点,那么只有当根结点右子树中存在小于等于key的结点时,小于等于key的最大键才会出现在右子树中,否则根结点就是小于等于key的最大键。
  • 具体实现如下:
public Key floor(Key key){
    Node x = floor(root, key);
    if (x == null) return null;
    return x.key;
}

private Node floor(Node x, Key key){
    if (x == null) return null;
    int cmp = key.compareTo(x.key);
    if (cmp == 0) return x;
    if (cmp < 0) return floor(x.left, key);
    Node t = floor(x.right, key);
    if (t != null) return t;
    else return x;
}

3.2.3.5 删除最大键和删除最小键

  • 对于deleteMin(),我们要不断深入根结点的左子树中,直至遇见一个空链接,然后将该结点的连接指向该节点的右子树(只需要在递归调用中返回它的右连接即可)。
  • 具体实现如下:
public void deleteMin(){
    root = deleteMin(root);
}

private Node deleteMin(Node x){
    if (x.left == null) return x.right;
    x.left = deleteMin(x.left);
    x.N = size(x.left) + size(x.right) + 1;
    return x;
}

3.2.3.6 删除操作

  • 对于拥有两个子结点的结点,在删除结点x后,用它的后继结点填补它的位置。因为x有一个右子结点,因此它的后继结点就是其右子树中的最小结点。具体步骤如下:
    • 将指向即将被删除的结点的链接保存为t
    • 将x指向它的后继结点min(t.right)
    • 将x的右链接(原本指向一颗所有结点都大于x.key的二叉查找树)指向deleteMin(t.right),也就是在删除后所有结点仍然都大于x.key的子二叉查找树
    • 将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) x.left = delete(x.left, key);
    else if (cmp > 0) x.right = delete(x.right, key);
    else{
        if (x.right == null) return x.left;
        if (x.left == null) return x.right;
        Note t = x;
        x = min(t.right);
        x.right = deleteMin(t.right);
        x.left = t.left;
    }
    x.N = size(x.left) + size(x.right) + 1;
    return x;
}

3.2.3.8 性能分析

在一颗二叉查找树中,所有操作在最坏情况下所需的时间都和树的高度成正比。

posted @ 2021-01-29 23:47  一天到晚睡觉的鱼  阅读(73)  评论(0)    收藏  举报