我所认识的Hash

前言

​ 关于Hash表,是我们经常会碰到的数据结构。大多数时候,它能高效地解决我们的一些实际问题。当然,多数情况下时间和空间正如鱼和熊掌,不可兼得。只有在特定的情况下,我们去选择适合当前需求的数据结构和算法。

​ Hash表本质是可以理解为键值对的集合。key-value一一对应。


字符Hash的简单应用

  • 先来看这样一个简单的例子,有一个字符串,仅有大写字母’A'~'Z'组成,我们需要对这个字符串中所有的字母进行统计。然后输出对应的表,如 ACAADEB, 则A->3, B->1, C->1, D->1, E->1。
  • 那么如何来做这个统计呢,这时候Hash表就可以派上用场了。字符‘A'对应的ASCII码为65,那么根据Hash的思想,我们可以把一个数组中的下标映射为字符,将下标对应的值中存储我们的统计结果,比如Hash['A'] = 3;
int hash[26];
string str = "ACAADEB";
void count_char_on_string(string &str) {
    for (int i = 0; i < str.length(); ++i) {
        ++hash[str[i] - 'A'];
    }
}

通过以上这个代码,我们就能将字符串中的字符一一统计下来。贯彻的思想就是将字符映射成数组下标


字符串Hash

  • 既然能够将字符映射成整数,那么肯定有办法将字符串也映射成整数。
  • 关于字符串hash算法,有很多种,可以通过这篇博客了解,不同的字符串hash算法的对比。各种字符串Hash函数比较
  • 此处,我仅仅以BKDRHash算法作为例子,核心的思想是通过一个选择器,我们也成为种子(seed),通常选择种子应该是一个质数,如31, 131,1313等,这样的好处在于减少不同字符串映射为相同整数的冲突。通过一个计算公式,将不同的字符串进行转化为数字。
// BKDR Hash Function
unsigned int BKDRHash(char *str) {
    unsigned int seed = 131; // 31 131 1313 13131 131313 etc..
    unsigned int hash = 0;
    while (*str) {
        //每转化一位字符,用当前的hash值 * seed,在加上字符的ASCII码。
        //hash * seed, 此处我们可以理解为当前字符串在前一个字符串hash值的基础上,偏移了一个种子的数级距离。
        hash = hash * seed + (*str++);
    }
    return (hash & 0x7FFFFFFF); //最后用hash & Ox7FFFFFFF 确保hash值在unsigned int 范围中。
}

利用字符串Hash可以解决很多问题,可以做字符串匹配。效率也蛮高的,相比KMP算法或者后缀数组和AC自动机等数据结构,它也不失为一种巧妙的办法。我们可以通过将每一位计算得到的hash偏移值存储在一个数组里,然后通过计算偏移得到子串的hash值,在和匹配串的hash值进行对比,如果相同,则说明匹配成功。

typedef unsigned long long ull;
const int MOD = 0x7FFFFFFF;
ull hash[1000]; //假设字符串长度小于1000
ull char_hash[1000]; //存储对应字符的hash值
inline ull cal_hash(string &str, const int l, const int r) {
    if (l == 0) return cal_hash[r];
    ull seed = 131;
    hash[0] = 1;
    for (int i = 1; i < str.length(); ++i) {
      hash[i] = hash[i-1] * seed;   //计算偏移
    }
    //用当前r对应的char_hash值-减去当前子串前一部分多计算的偏移hash值。就能得到字串的hash值了。
    return (char_hash[r] - char_hash[l-1] * hash[r-l+1]) + MOD) % MOD;
}

解决字符串Hash冲突

  • 或多或少,BKDRHash算法在极小的几率下会出现Hash冲突,解决的办法有很多,多数时候,我们采用邻接表来解决这个问题。
  • 由于算法涉及&操作,会有导致冲突的可能。在多串匹配的情况下,我们需要将所有匹配字符串计算得到的hash值,存放到邻接表中,所谓的邻接表就是数组套链表。将计算到的匹配串hash值作为数组中的值,并加入到对应的链表中,如果我们计算到的hash值出现冲突的情况,就往对应链表中加入新的节点。在后续匹配的情况下,进行查找链表并做多一次简单的匹配即可。

关于Hash的一个有趣应用

  • 有这么一个4 * 4的棋盘,棋子有两种状态,黑色或白色。我们这个时候也可以通过Hash来记录当前棋盘的信息。

那么对应的棋盘,我们用1表示黑色,用0表示白色。那么就有:

img
img

这个图转化成矩阵就是:

​ 1 0 1 0

​ 0 0 0 0

​ 1 1 0 1

​ 1 0 0 1

那么如何转化成整数来记录棋盘呢?

​ 2^0 2^1 2^2 2^3

​ 2^4 2^5 2^6 2^7

​ 2^8 2^9 2^10 2^11

​ 2^12 2^13 2^14 2^15

最后得到的棋盘我们可以通过判断当前棋子颜色,如果是黑色,则在对应位n上加上对应的2^n, n可以通过行列式计算出来。

int state[4][4]; //预处理记录棋子状态
unsigned int hash = 0;
for (int i = 0; i < 16; ++i) {
    hash += state[i/4][i%4] * (1 << i);
}

这样我们就把棋盘信息转化成一个整数了。


最后

​ 关于Hash表仍有很多的应用,还有一种应用是搜索领域,如何将互联网上大量的字符串存储,这里推荐大家了解一下字典树,它也是基于Hash算法的思想的数据结构。在iOS中,NSDictionary就是基于Hash的实现。以后有机会会深入CF框架看看实现源码,到时候再来分享。

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

推荐阅读更多精彩内容

  • 第一章 Nginx简介 Nginx是什么 没有听过Nginx?那么一定听过它的“同行”Apache吧!Ngi...
    JokerW阅读 32,636评论 24 1,002
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,562评论 18 399
  • 作者:July、wuliming、pkuoliver 说明:本文分为三部分内容,第一部分为一道百度面试题Top K...
    cyj_ya阅读 797评论 0 0
  • 大四下学期闲得要死,为了给自己的存在找点意义,在平安呆了两个月。在那里,认识了一个别人口中的特别文艺女青年的...
    任任任啊任阅读 302评论 0 0
  • 三毛认识荷西的时候,荷西高三,三毛大三。他们在西班牙相识,但三毛从未想过,日后这个人会成为她生命中十分重要的人。 ...
    谢先生读书会阅读 1,705评论 0 7