1.哈希hash引入
小集合元素M在大集合N中的出现次数。
如果二重循环遍历,时间复杂度O(NM),N、M大时很大
用空间换时间:
一个bool型数组hashTable[MAX](若需要出现次数则换成int)
用此数组值标记是否出现过(数组读入时):hashTable[x]=true;……………………O(N)
输出出现过的元素:判断是否true……………………………………………………O(M)
总时间复杂度O(M+N)
方法:直接把输入的数作为数组的下标X来对这个数的性质进行统计
但是,上面的输入的数每一个都是不超过10^5的整数,如果需要统计的是一个巨大的数,甚至是一个有意义的字符串,(字符串和极大数)无法记为下标
我们希望将需要统计的元素转化为可以唯一标识的较小整数,就可以通过下标计数了
即由key->H(key),key值变为其单值函数值,将单值函数值作为数组下标
2.常见hash函数:
直接定址法,除留余数法,平方取中法(少用)
1)直接定址法->恒等变换
H(key)=key
H(key)=a*key+b
2)除留余数法
H(key)=key%mod
通过此法,可以将key转换为不超过mod的整数,这样就可以将它作为数组下标。
/*注意表长TSize(即下标最大值)必须不小于mod,否则会产生越界。
当mod为素数,H(key)能尽可能覆盖[0,mod)内每一个数。*/
一般为了方便,TSize取为素数,mod取为等于mod
显然,通过%mod方法,可能会存在H(key)相同,但原本key值不同的情况——称为“冲突”
##解决冲突的方法:
——1开放定址法(线性探查法、平方探查法) 计算新的hash值
*线性探查法:
当得到key的hash值H(key),但是表中下标为H(key)已被占用时,
检查H(key)+1是否被占,直到探查到未被占的位置。
如果检查过程超过了表长,则回到表头,直到找到一个可使用的位置或者发现表已经被占满。
缺点:易导致扎堆,即表中连续若干个位置都被使用,这在一定程度上会降低效率。
*平方探查法:
为避免扎堆现象,
依次检查H(Key)+1²、H(Key)-1²、H(Key)+2²、H(Key)-2²…………
如果过程中H(key)+k²超过了表长,则将其对表长取模,
如果过程中H(key)-k²<0(假设首位为0),则取
((H(key)-k²) % TSize + TSize)%TSize
为了避免负数的麻烦,可以只进行正向的平方探查。
——2拉链法
不用重新计算新的hash值,而是把所有H(key)相同的key连接成一条单链表
一般来说,可以使用标准库模板库中的map来直接使用hash的功能(C++11以后可以用unordered_map速度更快)