查找(二):有序符号表

1.2 有序符号表

一个无序的符号表几乎是不可用的,因为其插入或查询的时间复杂度总是O(N)。对于大量的数据的操作性能十分低下。因此,实现一个有序的符号表十分必要。

1.2.1 一个有序符号表API
方法名称功能
void put(Key key,Value val)将键值存入表中(若值为空则将键key从表中删除)
Value get(Key key)获取键key对于的值(若key不存在则返回null)
void delete(Key key)从表中删去键key(及其对于的值)
boolean contains(Key key)键key是否存在于表中
boolean isEmpty()表是否为空
int size()表中键值对的数量
void rank(Key key)小于key的键的数量
void select(int i )选择排名为i的键

1.2.2 基于有序数组的二分查找符号表

有序数组二分查找依赖于一对平行数组Key[],Value[]。对Key进行排序时,也要对Value进行相应的移动。
二分查找符号表的核心方法是rank(Key key)。此方法返回的是比key小的元素的数量,在数组中即key应该存储的位置。
因此在插入操作时需要考虑到这种情况:

key比符号表中所有键都大,返回N,数组不用移动。

public int rank(Key key){
 '''N为现在符号表内键值对的数量'''
    int lo = 0, hi = N - 1;
    while(lo <= hi){
        int mid = lo + (hi - lo) / 2;
         '''通过compareTo方法比较大小,key大返回值大于0,key小返回值小于0,相等返回0'''
        int cmp = key.compareTo(keys[mid]);
        if(cmp < 0) hi = mid - 1;
        else if(cmp > 0) lo = mid + 1;
        else return mid;
    }
    return lo
}

下图是基于有序数组的符号表实现的轨迹图

有序数组二分查找排名轨迹


1.2.3 二分查找符号表部分实现
public class BinarySearchST<Key extends Comparable<Key> , Value> {
    private Key[] keys;
    private Value[] vals;
    private int N;
    public BinarySearchST(int capacity){
        this.keys = (Key[]) new Comparable[capacity];
        this.vals = (Value[]) new Object[capacity];
        this.N = 0;
    }
    public void put(Key key,Value val){
        int i = rank(key);
        //存在则覆盖
        if(i < N && keys[i].compareTo(key) == 0){
            vals[i] = val;
            return;
        }
        //不存在则插入,插入前先将插入位置后的元素后移
        for( int j = N;j > i; j--){
            keys[j] = keys[j-1];
            vals[j] = vals[j-1];
        }
        //后移结束,插入元素
        keys[i] = key;
        vals[i] = val;
        N++;
    }
    public Value get(Key key){
        if(isEmpty()) return null;
        int i = rank(key);
        if(i < N&&keys[i].compareTo(key) == 0) return vals[i];
        else return null;
    }
    public boolean contains(Key key){
        return this.get(key) != null;
    }
    public boolean isEmpty(){
        return N == 0;
    }
    public int size(){
        return N;
    }
    public Key min(){
        return keys[0];
    }
    public Key max(){
        return keys[N-1];
    }
    public int rank(Key key){
        '''已在上方实现量'''
    }
    //rank方法的递归实现
    public int rank(Key key,int lo,int hi){
        if(hi < lo) return lo;
        int mid = lo + (hi - lo)/2;
        int cmp = key.compareTo(keys[mid]);
        if(cmp > 0) return rank(key,mid+1,hi);
        else if(cmp < 0) return rank(key,lo,mid-1);
        else return mid;
    }
    public Key select(int k){
        return keys[k];
    }
}

1.2.4 二分查找性能分析

二分查找方法操作成本

方法名称时间复杂度
void put(Key key,Value val)N
Value get(Key key)logN
void delete(Key key)N
boolean contains(Key key)logN
boolean isEmpty()1
int size()1
void rank(Key key)lgN
void select(int i )1

简单符号表实现的成本总结:

算法查找(最坏)插入(最坏)
顺序查找(无序链表)NN
二分查找(有序数组)lgN2N

分析结果

1.在N个键的有序数组中进行二分查找,最多需要(lgN+1)次比较(无论是否成功)
2.在大小为N的有序数组中插入一个新的元素,在最坏情况下需要访问~2N次数组
3.在一个空表中插入N个元素在最坏情况下需要访问~ N2 次数组


结论

基于有序数组的二分查找符号表在查询方便确实有很大的提高,但是插入操作还是很慢,不适用于大规模的数据处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值