原题目链接:
https://leetcode.com/problems/two-sum/description/
题目描述:
给定一个整数数组,返回两个数字的索引,使它们相加得到一个特定的目标值。
你可以假设每个输入都只有一个解决方案,而您可能不会使用相同的元素两次。
实例:
Given nums = [2, 7, 11, 15], target = 9, Because nums[0] + nums[1] = 2 + 7 = 9, return [0, 1].
看到这是一道难度为easy的题,点开一看果然不出所料看上去就很easy,于是二话不说用了一种最简单的办法直接提交了上去。
class Solution {
public:
vector<int> twoSum(vector<int> &numbers, int target) {
vector<int> results;
for (int i = 0; i < numbers.size() - 1; i++) {
for (int j = i + 1; j < numbers.size(); j++) {
if (numbers[i] + numbers[j] == target) {
results.push_back(i);
results.push_back(j);
return results;
}
}
}
return results;
}
};
算法的核心思想是使用两层for循环暴力求解,第一层for循环选定左值,然后用第二层for循环寻找与左值相加为目标值的右值,如果没有找到,则使用下一个左值直到完成对结果的查找。
这份代码提交后虽然通过了检查,但是用时分布上排名靠后,说明算法还有优化的空间。
于是,基于学习的目的,我开始思考如何提高查找的效率。
经过对源代码的分析,显然,这是一个时间复杂度为O(n^2)的算法,那么能不能用更好的方法解决问题从而使算法的时间复杂度降低到时间复杂度为O(nlogn)甚至是O(n)呢?
在网上学习了别人的思想后,我总结出了两种解法,一是对给定数组的值和下标组合成的数据结构进行快速排序,然后使用二分查找找到题解,但是这样做需要耗费大量的编程时间,虽然能达到O(nlogn)级别的时间复杂度,但是对于比较小的测试数据集,无疑会耗费更多不必要的时间。
而网上的论坛提供了一种更好的解决方法,即使用map建立索引,使查找的时间复杂度降低到O(n),而且还不需要使用大量的额外代码。
代码如下:
class Solution {
public:
vector<int> twoSum(vector<int> &numbers, int target) {
int sum;
vector<int> results;
map<int, int> hmap;
for (int i = 0; i < numbers.size(); i++) {
if (!hmap.count(numbers[i])) { //如果该值的索引还未建立
hmap.insert(pair<int, int>(numbers[i], i)); //则插入(值,下标)
}
if (hmap.count(target - numbers[i])) { //如果找到了右值
int n = hmap[target - numbers[i]]; //获取右值对应的数组下标
if (n < i) {
results.push_back(n);
results.push_back(i);
return results;
}
}
}
return results;
}
};