昨天晚上,被一道LeetCode上名叫Palindrome Pairs困扰了很久,导致没时间写点什么。
坚持,不管学的多少,争取每天进步一点,记录一点。
今天用到了c++STL的map容器,了解到map容器内部存储是用红黑树,可以使查找访问map中元素的时间从线性结构的O(n)减少到O(logn)。
关于红黑树之前了解较少,今天学习了一部分知识,但还没完全领悟,下次再仔细总结。
----------以上都是废话。。忽略。。。
从O(n)到O(logn),树结构让数据查找更快捷,但树的构造和维护也牺牲了时间,但对于频繁访问的数据,这点牺牲是值得的。
树形结构存储不是查找数据最快的。
最快的是哈希表存储。
哈希表,简单来说,就是把要存储的数据的关键字,通过一个函数映射到对应的有限、连续的内存单元中存储。
举个例子来说:
有数据:char s[5]={'a' ,'b' ,'c' , 'd' , 'e'}
将每个元素通过 H(x)=(x-‘a’)%5 映射到0,1,2,3,4的内存单元中。
当你要查找‘d’在不在你的s数组里时:
只需计算H( ‘d' )地址4,看这块地址是否有’d‘。
有了哈希表,就比直接遍历s数组快多了。
而这只是理想的运气好的情况。应为会有冲突---------
假设s数组是这样 char s[6]={'a' ,'b' ,'c' , 'd' , 'e' , ’f‘},
H(x)不变:H(x)=(x-‘a’)%5 ,
那么当存入’f‘时会发现H('f')=0,而0内存单元已经被’a‘占据了。
这就是发生了冲突。
理论上讲哈希表的冲突是不可避免的,但要尽量减少冲突。
当冲突发生了想办法解决冲突。
减少冲突最直接的方法就是,选好用什么哈希函数。常见的哈希函数构造:
1.直接定值:
H(x)=x或者H(x)=a*x+b
这种方法就直接把数据的关键字一对一映射,若没有重复关键字,就没有冲突。
当数据很少,数量有限时适用,但在解决实际问题中用得少。
2.数字分析:
选取数字序列的某几位作为地址。
例如
若有下6个数据:
1234567
1325467
1212567
1267467
1293567
1388467
就可以选第3、4位(一定映射后)作为地址,会比选其他1/2/5/6/7位做地址的冲突少。
3.平方取中:
将数据的关键字平方取中间几位作为地址,这种方法常见。这样能使地址有更高随机性。具体取几位要看哈希表表长。
4.折叠法:
也是常见方法之一。
将数据的关键字折叠:
如有这样一个关键字:12345678
折叠为两位:(s形折叠)
12
34
56
+ 78
----------------
80
折叠为四位:(c形折叠)
1234
+ 8765
----------------
9999
5.取余法:
H(x)=x%d
d取值非常重要!这个方法的好坏就取决于d。
常见的d取质数或者 不包含小于20质因数的合数。
6.随机数生成:
用一个随机函数,取关键字的随机函数值作为地址。
适用于关键字位数或者形式不同的情况。
而解决冲突的发放也有几种。(电脑没电了,下次继续)