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.
"""1. 两重循环暴力搜索,236 ms
时间复杂度: O(n^2);空间复杂度:O(1),没占用额外的存储空间
std::vector::push_back
Adds a new element at the end of the vector, after its current last element.
This effectively increases the container size by one, which causes an automatic reallocation of the allocated storage space if -and only if- the new vector size surpasses the current vector capacity.
"""
#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> result;
for (int i = 0; i<nums.size(); i++)
{
for (int j = i + 1; j<nums.size(); j++)
{
if (nums[i] + nums[j] == target)
//or nums[j] == target - nums[i]
{
result.push_back(i);
result.push_back(j);
return result;
}
}
}
}
int main()
{
static const int myints[] = { 3, 2, 4 };
vector<int> nums;
nums.assign(myints, myints + 3);
int target = 6;
vector<int> result = twoSum(nums, target);
cout << result[0] << result[1] << endl;
system("pause");
}
更快的算法需要用到哈希表,先复习一下算法导论(只记录一些基本概念和结论,性能分析书上写的比较清楚了):
数组的直接寻址,可以在O(1)的时间访问任意元素。但是直接寻址法可能浪费了存储空间,因为实际需要存储的关键字集合T可能少于全域U,而U也可能非常大,使得存储变得不可能。
散列函数可以缩小需要处理的下标范围。将关键字为 K的元素放到 h(k) 中,也就是利用散列函数 h, 根据关键字 k计算出槽的位置。函数 h将关键字域 U映 射到散列表 T[0,1,…,m−1]的槽位上,这样需要处理的下表就是0:m-1。
好的散列函数特点:每个关键字都等可能地散列到 m个槽位的任何一个之中去,并与其他的关键字已被散列到哪一个槽位中无关(简单一致散列)。
常见的散列方式如除法散列法:h(k)=k mod m。两个关键字可能映射到同一槽上,这种情况称发生了碰撞。此时可以将散列到同一槽中的所有元素都放在一个链表中(chaining)。查找,插入和删除的平均时间复杂度都是O(1)。另一种碰撞处理是开放寻址法,对于每一个关键字 k,使用开放寻址法的探查 ,使用开放寻址法的探查 序列为:h(k,0) , h(k,1) ,∙∙∙, h(k,m − 1)
当散列表逐渐填满时,每个表位最终都可以被考虑为用来插入新关键字的槽。
"""2. Two-pass Hash Table, 15 ms
主要考虑利用哈希表来将(前面对j的循环)查找时间从O(n)降低到O(1)。这样整体的时间复杂度会降低到O(n),哈希表的空间复杂度为O(n)。
c++参考
http://www.cplusplus.com/reference/unordered_map/unordered_map/find/
http://en.cppreference.com/w/cpp/container/unordered_map
"""
#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;
vector<int> twoSum(vector<int>& nums, int target)
{
vector<int> result;
unordered_map<int, int> map;
for (int i = 0; i < nums.size(); i++)
{
map.insert({ nums[i], i });
}
for (int i = 0; i < nums.size(); i++)
{
int complement = target - nums[i];
auto j = map.find(complement);
if (j != map.end()) #注意处理异常
{
int j_index = j->second;
if (j_index != i)
{
result.push_back(i);
result.push_back(j_index);
return result;
}
}
}
}
int main()
{
int myints[] = { 3, 2, 3 };
vector<int> nums;
nums.assign(myints, myints + 3);
int target = 6;
vector<int> result = twoSum(nums, target);
cout << result[0] << result[1] << endl;
std::unordered_map<int, char> example = { { 1, 'a' }, { 2, 'b' }, {2, 'c'} };
auto search = example.find(2);
if (search != example.end()) {
std::cout << "Found " << search->first << " " << search->second << '\n';
}
else {
std::cout << "Not found\n";
}
std::unordered_map<int, char> map = { { 1, 'a' }, { 1, 'b' }, { 1, 'd' }, { 2, 'b' } };
auto range = map.equal_range(2);
cout << range.first->first;
system("pause");
}
"""3. One-pass Hash Table, 6 ms, beats 76.26%
上面用了两个循环,可以用一个循环同时做构建哈希表和进行查找这两件事。
"""
#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;
vector<int> twoSum(vector<int>& nums, int target)
{
vector<int> result;
unordered_map<int, int> map;
for (int i = 0; i < nums.size(); i++)
{
map.insert({ nums[i], i });
int complement = target - nums[i];
auto j = map.find(complement);
if (j != map.end())
{
int j_index = j->second;
if (j_index != i)
{
result.push_back(i);
result.push_back(j_index);
return result;
}
}
}
}
int main()
{
int myints[] = { 3, 2, 3 };
vector<int> nums;
nums.assign(myints, myints + 3);
int target = 6;
vector<int> result = twoSum(nums, target);
cout << result[0] << result[1] << endl;
system("pause");
}