哈希——无冲突应用

众所周知,哈希的速度是灰常快的,敢号称时间复杂度为O(1)的,呵呵,它和快排有一拼(目前只有快排敢叫“快排”这个名号)。而且,它偏偏还非常好用,这注定了它不平凡的存在。倒是目前网说哈希时,多是介绍各种哈希函数的构造及如何避免冲突,然后动不动就扯到MD5这些东西上去了。照这样说来,貌似哈希不常用啊。其实说白了,管他那么多干嘛,就不冲突着用嘛,自然就省去了那些“拉链门”啊神马的东西了。平时没事来个哈希就用用,速度那么快的说。

在这篇文章中说到的东西呢,用到的基本上全是不会出现冲突的哈希,也就是人们常说的“直接定址法”,这里说一下它在平时程序中的应用(注:本文中所有代码均为C代码;再注:本人是水人,所以所举例也全是水题,大神就掠过吧,呵呵)。这里只为抛砖,有玉的尽管砸来吧!

先来看看这个常见的东西吧:

如果n大于5就输出”Yes”,否则输出”No”,不用说,马上写出了这样的语句:

if (n > 5) {
	printf("Yes");
} else {
	printf("No");
}

呃,如果写成一行也就是:

printf(n > 5? “Yes”: “No”);

那现在,用哈希这样写:

char hash[2][] = { “No”, “Yes” };
printf(hash[n > 5]);

呵呵,看着有点怪,而且还不如上面写成一行来的方便,莫急,在这用哈希确实是有点那啥了,嘿嘿。那看下面这些题吧。

1.按ascii码哈希

字符串中去掉指定字母。

例如:s:”hello world”去掉t:”aeiou”中字母后为”hll wrld”。

相信这种简单问题,对高手来说不是问题,但咱就从这说起。

首先说这个题一般人会这样写:

char s[] = "hello world";
char t[] = "aeiou";
int i, j, len;
for (i = len = 0; s[i]; i++) {
	for (j = 0; t[j]; j++) {
		if (s[i] == t[j])
			break;
	}
	if (!t[j])
		s[len++] = s[i];
}
s[len] = 0;
printf(s); 

即用一个for循环(或一个if(s[i] != ‘a’ ...))了事,要不,用哈希试试?

char s[] = "hello world";
char t[] = "aeiou";
char hash[128];
int i, len;
memset(hash, 0, sizeof(hash));
for (i = 0; t[i]; i++) {
	hash[t[i]] = 1;
}
for (i = len = 0; s[i]; i++) {
	if (!hash[s[i]])
		s[len++] = s[i];
}
s[len] = 0;
printf(s);

貌似没减少多少代码,反倒多用了好多空间,但是时间复杂度降低了啊。

由上面这个例子看来,哈希表(不若说成是集合)的初始化是很简单方便的,而且在用的时候和初始化时操作都是一样的,都是通过hash[s[i]]实现的。所以基本上这也可以当作是一个模版来用的。

说到这,可以拿道poj水题看一下。poj2339 Rock, Scissors, Paper就是个石头剪刀布来回换,模拟一下就完了。主要就是判断周围四边的字符,如果就那么写的话,咳咳,如果我没算错,要写上3*4=12个或写一起也得3个长长的if吧,估计要写晕菜,不若来张哈希表,只要1个if就搞定了:

// 这些是初始化,也就短短几行
char t[128];
t['R'] = 'P';
t['P'] = 'S';
t['S'] = 'R';
// 判断上下左右有没有相克的,下面这些单独写个函数就行了
char c = t[s[i][j]];
if (s[i-1][j] == c
	|| s[i+1][j] == c
	|| s[i][j-1] == c
	|| s[i][j+1] == c)
	return c;
else
	return s[i][j]; 

哈,是不是一下就感觉代码量锐减呢,呵呵,而且还不会对速度上有影响,何乐而不用之呢?这里再说一下天梯1487,用个哈希多水的就过了,时间也不慢呀。1230就更不用说了,绝对的哈希啊。

2.按位哈希

嗯,这里就直接借鉴一下poj2777 Count Color好了,相信这道水题大家还是有目共睹的,也相信大家都看得出来是按位判断的,所以在这就好办了,每个颜色对应一位,区区30位,一个int就足够了。正如大家所想的那样,按位哈希,咳,说白了,就是为了节约空间才用到的,但刚好这道题用按位哈希反倒更好。(再插一句嘴,这题大家都知道用线段树,但在求有多少种颜色时,可以借用到树状数组的lowbit来计数。空间有限,这里不贴代码了,给个网址自己看吧:http://blog.csdn.net/sg_siqing/article/details/12209027

但是按位哈希有一个不好的地方就是它每一位能且只能表示两种状态,这就对它的使用造成了一定的限制,所以它的适用范围是:有且只有两种情况出现,才能用。而且还有个缺点,就是它必然要用到哈希函数去算位,在这点上不如上面可以直接用ascii码直接当下标的用。所以在这并不推荐这种用法,除非就是为了节约那7/8的空间。

3.数据结构

I)演变出的字典树

这里说的不是别人,就是大家熟悉的“字典树”,我左看、右看、上看、下看,怎么看它怎么是哈希,或许这就是传说中所谓的“多重哈希”吧。话说一张哈希表就够浪费空间的了,由这么多张哈希表组成的字典树岂不是浪费中的浪费?!但对于字符串来说,那查找效率,啧啧,没话说。

II)辅助的线性表

神马意思?哈希要和线性表一起用吗?是啊,有时不用冥思苦想哈希函数,直接拿个线性表来将结果存里边就完了,完事拿下标指过来就行了啊。这样算下来,嘿嘿,还真不用担心冲突呢,因为它永远不会冲突,来一个存一个,直接在线性表末尾添加。删除也方便,删一个就拿最后一个补过来就好了,时间复杂度还是O(1),只是改改指针,改改表长就完了嘛。再说了,删除的时候也不多啊,真删除的时候,还不定用着什么法呢,随机应变吧。

好了,多说无益,不如来道题看看啊。poj1451 T9,自认为是字典树题呀。两个字典树,一张哈希表,一张线性表水的(呵呵,大神莫笑)。首先让哈希表将两个字典树联系起来,再在第二个字典树结构中安插一个指针指向线性表,这样下来,基本上所有操作都是O(1)了。而这所有的一切,都是通过下标联系起来的。无独有偶的是,天梯1985GameZ游戏排名系统也是可以这样用的,以平衡二叉树为基,辅以哈希表,完爆无压力啊。

同样的,这里只给出代码的网址:

poj1451:http://blog.csdn.net/sg_siqing/article/details/12207153

wikioi1985:http://blog.csdn.net/sg_siqing/article/details/14647649

4.密码学应用

网上基本都会说的东西:MD5,都说这个东西是用的散列表,也就是哈希,我没写过,也没详细地看过,不清楚。但我写过一个叫DES加密的代码,这东西的代码里面绝对用的到哈希啊!有兴趣的可以看一下,这里不做过多阐述:http://blog.csdn.net/sg_siqing/article/details/21085471

5.冲突

冲突大多来自字符串的处理,而大多问题字符串数量在百万级别或以上,即最小106吧。而一个整数,却是可以存下231约109数量级大小的数,但开这么大的数组有点吃不消,那就折中一下吧,去网上找一个109在以内尽量不冲突的字符串哈希算法,这样哈希表容量大约是字符串量的100倍以上,冲突量会大大减小。计算出字符串的哈希值来,却不保存原字符串的情况下,用平衡二叉树来记录这些哈希值,便可以减小空间的开辟,但同时也将时间复杂度提高了一个logn。注意:这种方法不一定可行,只是个设想,因为即使100倍以上空间仍有可能出现冲突,这就取决于字符串哈希函数的计算问题了。

6.优缺点

先说缺点吧:

I)浪费空间严重,太严重了啊!!!;

II)乍看之下不容易理解,可读性低;

III)一般需要其他数据结构辅助,提供键列表或值列表。

……

再说说优点吧:

I)速度快,大家都知道的;

II)使用方便,操作复杂度基本上都是1;

III)汇聚或简化了代码,相似代码可以写成一句话,免去多个if或switch判断;

……

7.关键

大家都知道,二分查找时比较的是value,是值的大小,最后找到对应的下标。而相反的,哈希则是很直接地拿value值过来当下标用,一步到位找到对应的状态。所以,哈希的关键之处在于其值与下标之间的转换。

在本文伊始,就说道,这里不会说到冲突的处理方法,为什么呢?因为哈希本身是非常好用的东西,如果硬是要把冲突处理加进去,就会使代码量瞬间飙升,窃以为这是得不偿失的做法,反是只要不冲突时便用的十分顺心,而且代码量非常之少,非常之精简(难道好的算法都这样?!不解)。

这里再说几例:线段树和树状数组是对兄弟,而只要不用到更新区间且查询区间,相信更多的人会选择树状数组吧。同时树状数组中的lowbit还可以一步取得一个数二进制中最后一位1,这是线段树中所没有的;双栈(不是双向栈)可以做到O(1)实现空间的手工分配和回收,或栈中最大/最小问题;快排中的双指针可以实现在O(n)内找到前k大/小,或查找和为某值的两个数……诸此种种,尽显各种数据结构或算法之风骚,所以它们不擅长之处,就让那些擅长的去做吧!

8.哈希?

可能有人说了,这不是哈希,这个叫索引,我说,爱啥啥,关我什么事?我又不是去咬文嚼字地去给它个贴切的名字,我就叫它哈希,我用我的,叫自己的名,让别人说去吧,哈哈哈哈。。。。。。

哈希——无冲突应用

时间: 2024-10-24 13:51:08

哈希——无冲突应用的相关文章

命名空间与无冲突处理

JS编程中我们可能会遇到命名冲突的问题.命名冲突分两种情况来处理,对于模块内部,我们通常创建命名空间来解决:对于不同框架类库,我们采用无冲突处理(多库共存)的手段解决. 1.模块内部,注册命名空间 一个大模块可以细分成若干小模块,命名空间其实就是在大模块的作用域里面声明小的作用域,这样不同作用域之间同名标识符互不影响. 1)用立即执行函数表达式(IIFE)作为作用域. var a = 1; (function() { var a = 2; console.log(a);//2 })(); con

【模版】简单哈希和哈希表处理冲突

哈希(Hash)算法就是单向散列算法,它把某个较大的集合P映射到另一个较小的集合Q中.数学原理听起来很抽象,在网上找到一个很生动的描述.我们有很多的小猪,每个的体重都不一样,假设体重分布比较平均(我们考虑到公斤级别),我们按照体重来分,划分成100个小猪圈. 然后把每个小猪,按照体重赶进各自的猪圈里,记录档案.如果我们要精确找到某个小猪怎么办呢?我们需要每个猪圈,每个小猪的比对吗? 当然不需要了. 我们先看看要找的这个小猪的体重,然后就找到了对应的猪圈了. 在这个猪圈里的小猪的数量就相对很少了.

【翻译】理念:无冲突的扩展本地DOM原型

菜鸟翻译,望大家多多指正哈 原文:http://lea.verou.me/2015/04/idea-extending-native-dom-prototypes-without-collisions/ 理念:无冲突的扩展本地DOM原型 正如我昨天在博文中指出,我不喜欢使用jQuery的原因之一是因为它的包装对象.对于jQuery来说,这是一个明智的决定:早在2006年它被第一次开发出来的时候,IE有一个非常讨厌的内存泄漏bug,当我们给一个元素添加属性时它便很容易被引发出来.哦,那时我们还没有

C# Dictionary源码剖析---哈希处理冲突的方法有:开放定址法、再哈希法、链地址法、建立一个公共溢出区等

参考:https://blog.csdn.net/exiaojiu/article/details/51252515 http://www.cnblogs.com/wangjun1234/p/3719635.html 源代码版本为 .NET Framework 4.6.1 Dictionary是Hashtable的一种泛型实现(也是一种哈希表)实现了IDictionary泛型接口和非泛型接口等,将键映射到相应的值.任何非 null 对象都可以用作键.使用与Hashtable不同的冲突解决方法,D

哈希表冲突的两个解决方法线性探测和分离链接法

1.线性探测法 就是当要插入的行号发生冲突时,往下一个行号存放哈希值,直到没有冲突. 2.分离链接法 就是将一个行号做成链表的形式,如果有这个行号的冲突便新建一个节点将其插入这个行号的链表中. 在Mahout中,FastByIDMap是基于散列的,但它在处理散列冲突时使用的是线性探测,而非分离链接.因为线性探测不必为每个条目新增加一个额外的Map.Entry对象,节省了内存开销:在数据规模很庞大的时候,这种优势更加体现得出来.

解决哈希(Hash)冲突的主要方法

一)哈希表简介 非哈希表的特点:关键字在表中的位置和它之间不存在一个确定的关系,查找的过程为给定值一次和各个关键字进行比较,查找的效率取决于和给定值进行比较的次数. 哈希表的特点:关键字在表中位置和它之间存在一种确定的关系. 哈希函数:一般情况下,需要在关键字与它在表中的存储位置之间建立一个函数关系,以f(key)作为关键字为key的记录在表中的位置,通常称这个函数f(key)为哈希函数. hash : 翻译为"散列",就是把任意长度的输入,通过散列算法,变成固定长度的输出,该输出就是

关于CSS选择器优先级无冲突样式设置的展示

如果id和class都设置了同一样式,则显示id选择器的;如果id写的样式和class不冲突,则都会显示 .top{ border: 1px solid blueviolet; width: 438px; height: 50px; float: left; } #top{ padding-top: 15px; height: 35px; } <div class="top" id="top">    <a href="#"&g

哈希码冲突解法示意图

底层实现:ArrayList<Object>[]链表数组 先用hashcode找到元素所在链表位置, 再遍历链表通过equals()比较对应元素,避免冲突. java各种比较方式: hashCode()默认(Object)返回地址(地址相同肯定是同一对象) equals()默认比较地址是否相等 ==比较地址 原文地址:https://www.cnblogs.com/wenlin-gk/p/10088874.html

jQuery命名空间无冲突

var 2 // Map over jQuery in case of overwrite 3 _jQuery = window.jQuery, 4 5 // Map over the $ in case of overwrite 6 _$ = window.$; 7 8 jQuery.noConflict = function( deep ) { 9 if ( window.$ === jQuery ) { 10 window.$ = _$; 11 } 12 13 if ( deep &&