题目链接
1. Two Sum 难度:$\color{#00965e}{easy}$
知识点
1.哈希(散列)函数
经典散列函数Times33应用非常广泛,核心算法如下:
hash(i) = hash(i-1) * 33 + str[i]
laruence大佬有一篇文章讲过:PHP中的Hash算法 - 风雪之隅
2.哈希碰撞处理方法
2.1链地址法(拉链法)(广泛使用)
即使用单向链表来处理哈希冲突的key;php,java都是使用该方法
优点:
- 处理冲突简单,且无堆积现象;
- 平均查找长度短;
- 链表中的结点是动态申请的,适合构造表不能确定长度的情况;
- 节省空间,相对而言,拉链法的指针域可以忽略不计,因此较开放地址法更加节省空间。
- 插入删除方便,插入结点应该在链首,删除结点比较方便,只需调整指针而不需要对其他冲突元素作调整。
Tips:如果链表过长,查询效率会从O(1),变成遍历链表,效率降低,这个时候可以使用树结构,来代替链表,提高查询效率。
(由此引出另一个知识点:二叉树与二叉查找树),此处不赘述。
2.2开放寻址法
Hi=(H(key)+di) MOD m i=1,2,…,k(k<=m-1)
其中,m
为哈希表的表长。di
是产生冲突的时候的增量序列。如果di
值可能为1,2,3,…m-1
,称线性探测再散列。
如果di
取1
,则每次冲突之后,向后移动1
个位置,称线性探测再散列;
如果di
取值可能为1,-1,2,-2,4,-4,9,-9,16,-16,…k*k,-k*k(k<=m/2)
,称二次探测再散列;
如果di
取值可能为伪随机数列。称伪随机探测再散列。
缺点:
- 处理冲突简单,且无堆积现象;
- 平均查找长度短;
2.3再哈希法Rehash
当发生冲突时,使用第二个、第三个、哈希函数计算地址,直到无冲突时。缺点:计算时间增加。比如上面第一次按照姓首字母进行哈希,如果产生冲突可以按照姓字母首字母第二位进行哈希,再冲突,第三位,直到不冲突为止.这种方法不易产生聚集,但增加了计算时间。
2.4建立一个公共溢出区
假设哈希函数的值域为[0,m-1],则设向量HashTable[0..m-1]为基本表,另外设立存储空间向量OverTable[0..v]用以存储发生冲突的记录。
解法
1.暴力双循环略过不提;
2.哈希表(桶);
对于数组A,构造一个哈希,初次遍历时候就记录下值和数组下标,再遍历过程中,先判断哈希表里是否已经有target - A[i],有的话,直接返回就好了;
复杂度分析
- 时间复杂度:O(N),其中 N是数组中的元素数量。对于每一个元素 x,我们可以 O(1) 地寻找 target - x。
- 空间复杂度:O(N),其中 N 是数组中的元素数量。主要为哈希表的开销。
C语言没有hashTable结构,需要自行实现hashTable(知识点:如何实现hashTable插入/删除)这里使用PHP演示代码实现,其他语言类似,此处不赘述;
class Solution {
/**
* @param Integer[] $nums
* @param Integer $target
* @return Integer[]
*/
function twoSum($nums, $target) {
$len = count($nums);
$table = [];
$result = [];
//php数组的底层实现就是hash表,这里就不像c一样计算最小值那些了步骤了,直接一把梭
for($i=0;$i < $len;$i++) {
//先判断是否有,有的话直接返回下标
if (isset($table[$target - $nums[$i]])) {
return [$table[$target - $nums[$i]],$i];
}else{
//否则hash里的数字 => 下标
$table[$nums[$i]] = $i;
}
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。