散列表

什么散列

散列是提供对任何有名项提供存取操作和删除操作的列表。这种结构的目的是提供常数时间的基本操作。

举个例子:如果所有元素都是16-bits且不带正负号的整数,范围是0-65535。那么简单运用一个 array 就可以满足上面的期望。首先配置一个arrayA.,拥有65536个元素,初值全部为0,每个元素值代表相应元素出现的次数。如果插入元素i,就执行A[i]++,如果删除元素i,就执行A[i]--,如果搜素元素就检查A[i]是否为0。以上的每一个操作都是常数时间。这种解法的额外负担是array的空间和初始化时间。

绘图1.jpg

但是这种解法存在2个现实的问题:

  • 如果元素很多,那么要准备的数组大小就很大,空间占用太大。
  • 如果元素是其他的类型(非整型),那么就不能作为数组索引。

第一个问题:
我们是可以采用一个映射函数,将大数转换为小数,比如给定整数x,那么
x % array.size(),那么就会得到一个范围在 [ 0,array.size() - 1 ] 之间的数,也可以作为数组索引,但这会引入新的问题:如何解决碰撞冲突的问题。

第二个问题:
我们可以将一个非整数类型的转换为整型,比如字符串"string",转换为ASCII编码。

线性探测

当用散列函数计算出元素的插入位置,而该位置已经不能使用时,最简单的办法就是循序往下一一寻找,直到找到一个可用的位置为止。
元素的删除则可以采用"惰性删除",也就是标记删除记号,实际删除则待散列表重新整理时再进行。

线性探测.jpg

但线性探测会有一个不好的现象是:如图最后状态中,除非元素进过散列函数计算之后直接得出位置在#4 ~ #7,否则#4 ~ #7永远不可能会被优先考虑,因为(8 9 0 1 2已被占,遇到冲突会线性巡访整个表格) #3一直会被优先考虑。
这样会暴露出一个问题:主集团(primary clustering)陷阱。散列表中是一大团被用过的方格,插入操作极有可能在主集团所形成的泥泞中奋力爬行,不断解决碰撞问题,最后再找到空位置。然而插入之后又助长了主集团的长度。

public class LinearProbingHashST<Key, Value> {
    private int n;            // 当前key-value元素对数
    private int m;            // 线性表(key-value)的总长度
    private Key[] keys;     // the keys
    private Value[] vals;   // the values

    public void put(Key key, Value val) {
        .....省略参数的有效性判断
        // 如果已经用掉50%,则扩容,减小主集团的影响
        if (n >= m / 2)
          resize(2 * m);      // resize里面会重新计算散列值,插入元素
    
        // 线性探测过程
        for (int i = hash(key); keys[i] != null; i = (i + 1) % m) {
            // 如果已经存在key,则更新value
            if (keys[i].equals(key)) {
                vals[i] = val;
                    return;
            }
        }
        keys[i] = key;
        vals[i] = val;
        n++;
    }
    
    public void delete(Key key) {
        ...... 省略参数有效性判断
        // find position i of key
        int i = hash(key);
        while (!key.equals(keys[i]))
            i = (i + 1) % m;
        
      // delete key and associated value
        keys[i] = null;
        vals[i] = null;

        // rehash all keys in same cluster
        i = (i + 1) % m;
        while (keys[i] != null) {
            // delete keys[i] an vals[i] and reinsert
            Key keyToRehash = keys[i];
            Value valToRehash = vals[i];
            keys[i] = null;
            vals[i] = null;
            n--;
            put(keyToRehash, valToRehash);
            i = (i + 1) % m;
        }
        n--;
        // halves size of array if it's 12.5% full or less
        if (n > 0 && n <= m / 8)
            resize(m / 2);
    }
}

二次探测

二次探测主要用于解决主集团的问题。
其命名由来是因为解决碰撞的方程式:F(i) = i^2是一个二次方程式。更明确的说,如果散列函数计算出新元素位置H,而该位置实际上已经被使用,那么我们就依次尝试H+12,H+22,H+3^2......而不是像线性探测一样依序尝试:H+1,H+2,H+3.......

二次探测.jpg

二次探测可以消除主集团,但却可能造成次集团:两个元素经散列函数计算出来的位置若相同,那么插入时所探测的位置也相同,形成某种浪费。

但总体来说,还是二次探测相比于线性探测,仍然值得选择。

开链法

开链法是在每一个表格元素中维护一个list,散列函数分配某一个list,然后再那个list身上执行元素的插入,搜寻,删除等操作。虽然针对list是一种线性搜索,但list够短。速度还是很快。

开链法jpg

散列函数


参考资料
[1] 《STL源码剖析》侯捷
[2] 《算法》4th [美] Robert Sedgewick,Kevin Wayne

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

推荐阅读更多精彩内容

  • 数据结构与算法--散列表 之前学习了基于链表的顺序查找、基于有序数组的二分查找、二叉查找树、红黑树,这些算法在查找...
    sunhaiyu阅读 648评论 3 5
  • 散列表是支持 INSERT 、DELETE 和 SEARCH 的字典操作,其是对普通数组概念的推广,因为可以对数组...
    点融黑帮阅读 862评论 0 11
  • 本文主要介绍散列表(Hash Table)这一常见数据结构的原理与实现。由于个人水平有限,文章中难免存在不准确或是...
    absfree阅读 16,294评论 2 35
  • 什么是哈希表? 哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数...
    郝程序猿阅读 2,233评论 1 7
  • 概念 散列表的实现常常叫做散列(hashing)。散列是一种用于以常数平均时间执行插入、删除和查找的技术。散列函数...
    NoFacePeace阅读 326评论 0 0