查找(简单符号表实现)

符号表

简介

符号表是一个将键和值联系起来的数据结构。符号表能将一个键值对插入符号根据键查找到相应的值

API

public class ST<Key, Value>
{
            ST()              // 创建一个符号表
    void    put(Key key, Value val)     // 将键值对加入符号表中
    Value   get(Key key)       // 获取键对应的值
    void    delete(Key key)    // 从表中删除键key
    boolean contains(Key key)  // 键key时候在表中有对应的值
    boolean isEmpty()          // 符号表是否为空
    int     size()             // 符号表中的元素数量
    Iterable<Key> keys()       // 表中所有键的集合
}         

特性

  • 每个键都对应着一个值(表中不存在重复的键)
  • 向表中存入的键值对和已有的键冲突时,新的值会替代旧的值
  • 键不能为null

有序符号表

简介

在符号表的基础上,如果键都是Comparable对象,可以保持键的有序性,从而扩展其API

API

public class ST<Key extends Comparable<key>, Value> {
    ST()                             创建一张符号表
    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()                       表中的键值对数量
    Iterable<Key> keys()             表中的所有键的集合
        
    // 有序符号表额外的api
    Key min()                            最小的键
    Key max()                            最大的键
    Key floor(Key key)                   小于等于key的键的数量
    Key ceiling(Key key)                 大于等于key的键的数量
    int rank(Key key)                    等于key的键的数量
    Key select(int i)                    排位为i的键
    void deleteMin()                     删除最小的键
    void deleteMax()                     删除最大的键
    int size(Key lo,Key hi)              [lo,hi]之间键的数量
    Iterable<Key > keys(Key lo,Key hi)   [lo,hi]之间所有的键,已排序
    Iterable<Key > keys()                有序表中所有的键,已排序
}

符号表的实现

1. 基于无序链表的实现

实现方法

每个节点存储一个键值对,通过遍历链表,使用equals方法对键进行比较。
get方法:如果被查找的键和当前键相同,则返回当前键对应的值。如果链表中的所有键都匹配失败,则返回null。
put方法:如果被查找的键和当前键相同,则用值替换当前键所对应的值,并将原来的值返回。如果链表中的所有键都匹配失败,则创建一个新节点,并插入到链表的开头

基于链表的符号表的索引用例轨迹
代码实现
package edu.princeton.cs.algs4.chapter3;


/**
 * 使用无序链表实现的符号表
 * 一次查询和插入的平均时间复杂度都是N
 * Created by tianxianhu on 2017/3/6.
 */
public class SequentialSearchST<Key, Value> {
    private int N;
    private Node first;

    public SequentialSearchST() {
    }

    private class Node{
        Key key;
        Value value;
        Node next;

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

    public Value get(Key key) {
        if (key == null)
            throw new IllegalArgumentException("argument to get() is null");

        for (Node x = first; x != null; x = x.next) {
            if (key.equals(x.key)) {
                return x.value; // 命中
            }
        }
        return null; // 未命中
    }

    public void put(Key key, Value value) {
        if (key == null)
            throw new IllegalArgumentException("first argument to put() is null");

        if (value == null) {
            delete(key);
            return;
        }

        for (Node x = first; x != null; x = x.next) {
            if (key.equals(x.key)) {
                x.value = value; // 命中,替换
            }
        }
        first = new Node(key, value, first);// 未命中,新建节点
        N++;
    }

    public void delete(Key key) {
        if (key == null) throw new IllegalArgumentException("argument to delete() is null");
        first = delete(first, key);
    }

    private Node delete(Node x, Key key) {
        if (x == null) return null;
        if (key.equals(x.key)) {
            N--;
            return x.next;
        }
        x.next = delete(x.next, key);
        return x;
    }

    public int size() {
        return N;
    }

    public boolean isEmpty() {
        return size() == 0;
    }

}

2. 基于有序数组的实现

实现方法

使用一对平行数组,一个存储键,一个存储值
该实现的核心是rank方法,它返回表中小于给定键的键的数量
get方法:如果键存在于表中,使用rank方法返回其在数组中的索引。如果不存在,则返回null。
put方法:使用rank方法查找到该键值所在的数组索引,如果该位置的键就是要插入的键,则更新它的值。如果该位置的值不是要插入的值,则将该值后面所有的值向后移动,然后将此键值对插入到数组当中。

使用基于有序数组的符号表实现的索引用例轨迹
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, lo, mid - 1);
    else if (cmp > 0) 
        return rank(key, mid + 1, hi);
    else 
        return mid;
}

// 非递归的二分查找
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;
}
代码实现
package edu.princeton.cs.algs4.chapter3;

/**
 * 基于有序数组的符号表
 * Created by tianxianhu on 2017/3/6.
 */
public class BinarySearchST <Key extends Comparable<Key>, Value> {
    private Key[] keys;
    private Value[] vals;
    private int N;

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

    public int size() {
        return N;
    }

    public boolean isEmpty() {
        return N == 0;
    }

    public Value get(Key key) {
        if (null == key)
            throw new IllegalArgumentException("argument to get() is null");

        if (isEmpty())
            return null;

        int i = rank(key);
        if (i < N && keys[i].compareTo(key) == 0)
            return vals[i];
        else
            return null;
    }

    private int rank(Key key) {
        int lo = 0, hi = N - 1;
        while (lo <= hi) {
            int mid = (lo + hi) / 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;
    }

    public void put(Key key, Value val) {
        if (null == key)
            throw new IllegalArgumentException("argument to get() is null");

        if (null == val) {
            delete(key);
            return;
        }

        int k = rank(key);
        // 如果已经存在,进行更新
        if (k < N && keys[k].compareTo(key) == 0) {
            vals[k] = val;
            return;
        }
        // 不存在,先挪动数组,然后插入
        for (int i = N; i > k; i--) {
            keys[i] = keys[i - 1];
            vals[i] = vals[i - 1];
        }
        keys[k] = key;
        vals[k] = val;
        N++;
    }

    public void delete(Key key) {
        if (key == null) throw new IllegalArgumentException("argument to delete() is null");
        if (isEmpty()) return;

        // compute rank
        int i = rank(key);

        // key not in table
        if (i == N || keys[i].compareTo(key) != 0) {
            return;
        }

        for (int j = i; j < N-1; j++)  {
            keys[j] = keys[j+1];
            vals[j] = vals[j+1];
        }

        N--;
        keys[N] = null;  // to avoid loitering
        vals[N] = null;

        // resize if 1/4 full
        if (N > 0 && N == keys.length/4) resize(keys.length/2);

        assert check();
    }

    private boolean check() {
        return isSorted() && rankCheck();
    }

    // are the items in the array in ascending order?
    private boolean isSorted() {
        for (int i = 1; i < size(); i++)
            if (keys[i].compareTo(keys[i-1]) < 0) return false;
        return true;
    }

    // check that rank(select(i)) = i
    private boolean rankCheck() {
        for (int i = 0; i < size(); i++)
            if (i != rank(select(i))) return false;
        for (int i = 0; i < size(); i++)
            if (keys[i].compareTo(select(rank(keys[i]))) != 0) return false;
        return true;
    }

    public Key select(int k) {
        if (k < 0 || k >= size()) {
            throw new IllegalArgumentException("called select() with invalid argument: " + k);
        }
        return keys[k];
    }

    // resize the underlying arrays
    private void resize(int capacity) {
        assert capacity >= N;
        Key[]   tempk = (Key[]) new Comparable[capacity];
        Value[] tempv = (Value[]) new Object[capacity];
        for (int i = 0; i < N; i++) {
            tempk[i] = keys[i];
            tempv[i] = vals[i];
        }
        vals = tempv;
        keys = tempk;
    }
}

两种符号表实现的比较

简单符号表实现的成本比较
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容