传送门:https://leetcode.com/problems/two-sum/#/description
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
给定一个数字和一个目标数字,求出数组中两个数字之和等于给定的目标数字。
注意:假定每个数组中有且只有一组解,不能使用同一个数组元素两次
例如:数组[2,7,11,15], 目标数字9,因为nums[0] + nums[1] = 9,所以返回[0,1]
遍历数组法,直接遍历整个数组,找到两个数组元素和等于target即可。
通过建立 <数值,下标>
的哈希表,每遍历到一个元素 i
,就查找所对应的元素 target-i
是否在哈希表中,并且 数值 i
和 数值 target - i
不能是同一个下标。
==》时间复杂度:O(n2)
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> res;
for(int i = 0; i < nums.size(); i++)
{
for(int j = i + 1; j < nums.size(); j++)
{
if((nums[i] + nums[j]) == target)
{
res.push_back(i);
res.push_back(j);
break;
}
}
if(!res.empty())
break;
}
return res;
}
};
==》时间复杂度:O(n)
==》空间复杂度:O(n)——哈希表
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int i,sum;
vector<int> results;
map<int, int> hmap;
for(i = 0; i < nums.size(); i++){
hmap.insert(pair<int, int>(nums[i], i));
}
for(i = 0; i < nums.size(); i++){
if(hmap.count(target - nums[i]) && hmap[target - nums[i]] != i)
{
int n = hmap[target - nums[i]];
results.push_back(i);
results.push_back(n);
break;
}
}
return results;
}
};
注:使用count,返回的是被查找元素的个数。如果有,返回1;否则,返回0。注意,map中不存在相同元素,所以返回值只能是1或0。
提交结果
哈希算法(也称散列算法)
将任意长度的输入(又叫做预映射, pre-image)映射为固定长度的输出的映射规则。
==》散列值的空间通常远小于输入的空间
哈希值(也称散列值)
通过对原始数据映射后得到的输出。
哈希表
以 键-值(key-value)
存储数据的结构,只需输入待查找的 key,就可以查找其对应的值。
负载因子
例如要存储80个元素,但可能为这80个元素申请了100个元素的空间。80/100=0.8,这个数字称为负载因子。我们之所以这样做,也是为了“高速存取”的目的。
冲突
两个元素通过 哈希函数 得到同样的结果。
==》无论哈希文本长度,都可得到长度相同的哈希值;
==》两个相似的文本,得到的哈希值完全不同。
散列函数设计方法:数据分析法(从原始数据中截取部分作为散列函数)、直接寻址法、平方取中法、折叠法、随机数法等
两类方法:开放寻址法(open addressing)和链表法(chaining)
基本思想: 若存在散列冲突,则探测一个空闲位置,将其插入。
① 线性探测(Linear Probing)
从当前位置开始,依次往后(若不够则从头继续)查找,看是否有空闲位置,直到找到为止。
② 二次探测(Quadratic Probing)
区别: 步长变成了原来的“二次方“,即:探测的下标序列
hash(key)+0,hash(key)+12,hash(key)+22,hash(key)+32,……
③ 双重散列(Double hashing)
使用一组散列函数 hash1(key),hash2(key),hash3(key)……先用第一个散列函数,如果计算得到的存储位置已经被占用,再用第二个散列函数,依次类推,直到找找到空闲的存储位置。
当数据越来越多时,散列冲突发生的可能性就会越来越大,空闲位置会越来越少,线性探测的时间就会越来越久。极端情况下,最坏情况下的时间复杂度为 O(n)。同理,在删除和查找时,,也有可能会线性探测整张散列表,才能找到要查找或者删除的数据。
为了尽可能保证散列表的操作效率,利用装载因子(load factor)来表示空闲槽位的多少。
散列表的装载因子 = 填入表中的元素个数 / 散列表的长度
装载因子越大,说明空闲位置越少,冲突越多,散列表的性能会下降。
所有散列值相同的元素我们都放到相同槽位对应的链表中。
插入:通过散列函数计算出对应的散列槽位,将其插入到对应链表中即可
==》时间复杂度:O(1)
查找、删除:需要遍历,时间复杂度跟链表的长度 k 成正比,也就是 O(k)。
对于散列比较均匀的散列函数来说,理论上讲,k=n/m,其中 n 表示散列中数据的个数,m 表示散列表中“槽”的个数。
最常见的几种应用:安全加密、唯一标识、数据校验、散列函数、负载均衡、数据分片、分布式存储。