《算法》3.1符号表

1符号表

符号表就是一种存储键值对的数据结构,

支持两种操作:

  • 插入(put):即将一组新的键值对存入表中;
  • 查找(get):即根据给定的键得到相应的值。

1.1特点

  • 每个键只对应着一个值(表中不允许存在重复的键)
  • 当插入新的键值对与已有的键冲突时,新的值会代替旧的值
  • 键不能为空
  • 值不能为空
  • 删除的两种方法:
    • 延时删除:将值置为空,之后某个时间在删除
    • 即时删除:直接删除键值对

1.2有序符号表

许多符号表的实现都利用了Comparable接口带来的键的有序性来更好地实现put()get()方法。

2无序链表的顺序查找

每个结点存储一个键值对。

实现:

/**
 * 基于无序链表的顺序查找
 * @Author: AZhu
 * @Date: 2021/2/28 21:09
 */
public class SaquentialSearchST<Key, Value> {

    private Node first;//链表首结点
    
    //链表结点的定义
    private class Node {
        Key key;
        Value value;
        Node next;

        public Node(Key key, Value val, Node next) {
            this.key = key;
            this.value = val;
            this.next = next;
        }
    }

    /**
     * 查找给定的键,返回对应的值
     * @param key
     * @return
     */
    public Value get(Key key) {
        for (Node x = first; x != null; x = x.next) {
            if (key.equals(x.key)) {
                return x.value;//命中
            }
        }
        return null;//未命中
    }

    /**
     * 插入键值对,若已存在则更新值,否则新建结点插入。
     * @param key
     * @param value
     */
    public void put(Key key, Value value) {
        for (Node x = first; x != null; x = x.next) {
            if (key.equals(x.key)) {//命中,更新
                x.value = value;
                return;
            }
        }
        first = new Node(key, value, first);//未命中,新建结点插入链表开头
    }
    
}

性能:

  • 在一个含有N对键值对的基于无序链表的符号表中:
    • 未命中的查找和插入都需要N次比较
  • 命中的查找在最坏的情况下需要N次比较
  • 向一个空表中插入N个不同的键需要~N^2/2次比较。

3有序数组的二分查找

使用了一对平行数组,一个存储键,一个存储值。

核心是rank()方法:

实现:

/**
 * 基于有序数组的二分查找
 * @Author: AZhu
 * @Date: 2021/2/28 21:28
 */
public class BinarySearchST<Key extends Comparable<Key>, Value> {

    private Key[] keys;//键
    private Value[] values;//值
    private int N;//符号表的大小

    public BinarySearchST(int capacity) {
        keys = (Key[]) new Comparable[capacity];
        values = (Value[]) new Object[capacity];
    }

    /**
     * 返回表中键值对的数量
     * @return
     */
    public int size() {
        return N;
    }

    /**
     * 判断表是否为空
     * @return
     */
    public boolean isEmpty() {
        return size() == 0;
    }

    /**
     * 查找给定key所对应的value
     * @param key
     * @return
     */
    public Value get(Key key) {
        if (isEmpty()) return null;
        int i = rank(key);//查找
        if (i < N && keys[i].compareTo(key) == 0) {//找到了
            return values[i];
        } else {//找不到
            return null;
        }
    }

    /**
     * 插入键值对,若已存在则更新值,否则插入。
     * @param key
     * @param value
     */
    public void put(Key key, Value value) {
        int i = rank(key);
        if (i < N && keys[i].compareTo(key) == 0) {//找到了
            values[i] = value;//更新
            return;
        }
        for (int j = N; j > i; j--) {//找不到
            //后移
            keys[j] = keys[j - 1];
            values[j] = values[j - 1];
        }
        //插入
        keys[i] = key;
        values[i] = value;
        N++;
    }

    /**
     * 迭代的二分查找
     * @param key
     * @return
     */
    public int rank(Key key) {
        int lo = 0, hi = N - 1;
        while (lo <= hi) {
            int mid = lo + (hi - lo) / 2;
            int cmp = key.compareTo(keys[mid]);
            if (cmp < 0) {
                hi = mid - 1;
            } else if (cmp > 0) {
                lo = mid + 1;
            } else {
                return mid;
            }
        }
        return lo;
    }

    /**
     * 递归的二分查找
     * @param key
     * @param lo
     * @param hi
     * @return
     */
    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, lo, mid - 1);
        } else if (cmp > 0) {
            return rank(key, mid + 1, hi);
        } else {
            return mid;
        }
    }

}

性能:

  • 二分查找很快
  • N个键的有序数组中进行二分查找最多需要lgN+1次比较。
  • 但是put()方法太慢了:构造一个基于有序数组的符号表需要访问数组的次数是数组长度的平方级别的。

4总结

现代应用需要同时能够支持高效的查找和插入操作的符号表的实现。

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

算法最坏情况平均情况是否高效的支持有序性相关的操作
查找插入查找插入
顺序查找NNN/2N
二分查找lgN2NlgNN
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值