快速找出一个数组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个数的和