快速找出一个数组numbers中的两个数字,让这两个数字之和等于一个给定的值,假定该数组中存在至少一组符合要求的解。在leetcode上也有该题,描述为:Two Sum:Given an array of integers, find two numbers such that they add up to a specific target number.
编程之美上给出了两个可行的方法,下面分别描述
解放一:hash算法
简单的说就是对于每一个numbers[i],把tartget-numbers[i]存入hash表中,然后遍历数组numbers,对于numbers[i],如果发现numbers[i]存在于hash表中,则说明numbers中存在target-numbers[i]。对于此方法,如果hash表使用map实现,则时间复杂度是O(nlogn),虽然和解法二相同,但是在leetcode上提交仍然超时(可能是我写的有问题),如果使用std::tr1::unordered_map(一种hash_map),时间复杂度是O(n),但是leetcode不支持,这里使用std::tr1::unordered_map,只是为了说明该算法,大家也可以使用普通的map,代码如下:
#include <iostream>
#include <tr1/unordered_map>
#include <vector>
#include <assert.h>
using namespace std;
bool isExist(tr1::unordered_map<int,int>& hash,int value,int index)
{
tr1::unordered_map<int,int>::iterator iter = hash.begin();
for(;iter != hash.end();iter++)
{
if(iter->first == value && iter->second != index)return true;//判断value是否存在,加上index是为了防止两个加数相等但只出现一次,比如5+5==10,但只有一个5
}
return false;
}
vector<int> twoSum(vector<int> &numbers, int target) {
vector<int> res;
int i,size = numbers.size();
std::tr1::unordered_map<int,int> hash;
for(i=0;i<size;i++)hash[target - numbers[i]] = i;//把待查找的另一半加入hash
for(i=0;i<size;i++)
{
if(isExist(hash,numbers[i],i))//如果自己在hash中,则表明自己是另一半加入的,则另一半存在
{
int begin = hash[target-numbers[i]] > hash[numbers[i]] ? hash[numbers[i]] : hash[target-numbers[i]];
int end = hash[target-numbers[i]] > hash[numbers[i]] ? hash[target-numbers[i]] : hash[numbers[i]];
res.push_back(begin+1);//把两个数的位置存入结果
res.push_back(end+1);
break;
}
}
return res;
}
解法二:排序法
简单的说,就是对原数据进行排序,然后用两个指针从两头分别进行扫描,如果和大于目标,则后面指针前移,反之,前面指针后移。针对leetcode上需要求两个数下标的情况,则用结构体保存原来的下标,具体代码如下:
struct node
{
int value;
int index;//在数组中的原来位置
};
bool operator<(const node& a,const node& b)
{
return a.value < b.value;
}
vector<int> twoSum(vector<int> &numbers, int target) {
int i,j,size = numbers.size();
vector<node> data(size);
for(i=0;i<size;i++)
{
data[i].value = numbers[i];
data[i].index = i+1;
}
sort(data.begin(),data.end());
i=0;
j=size-1;
vector<int> res;
while(i < j)
{
if(data[i].value + data[j].value < target)i++;
else if(data[i].value + data[j].value > target)j--;
else
{
int begin = data[i].index > data[j].index ? data[j].index : data[i].index;
int end = data[i].index > data[j].index ? data[i].index : data[j].index;
res.push_back(begin);
res.push_back(end);
break;
}
}
numbers.clear();
return res;
}
代码如有问题,请指正,谢谢。下一篇会接着分析3个数的和、4个数的和