题目描述:(来源于乐扣1)
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
1 暴力解法
这道题和乐扣167题( 两数之和 II - 输入有序数组)类似,区别仅在于这道题的数组不是有序的。
暴力解法依然是双层遍历,然后比对计算两者之和。
从下面的程序可以看出,时间复杂度是
O
(
n
2
)
O(n^2)
O(n2)的。
class Solution {
public:
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){
result.push_back(i);
result.push_back(j);
return result;
}
}
}
return result;
}
};
运行结果:
上面的280ms是在if语句中加了return语句,因为题中说明了只会有一个答案,所以一旦找到一个,就可以提前返回。
下面的436ms是没有在if语句中加return语句,所以会遍历整个数组结束,耗时较长。
2 查找表法
如果我们打算采用类似于167号题的二分搜索法:固定一个元素,查找另一个元素,就必须要求数组是有序的。
我们可以先对数组排序,然后使用二分搜索法。但是问题在于,这道题要求返回的是索引,这样我们在排序时就需要记录元素的索引,比较麻烦。
查找表法:
将所有元素放入查找表中,之后对于每一个元素a,查找target-a是否存在。
1)使用set
使用set,我们就不能记录查找到的元素的索引,所以在找到答案时,我们需要遍历一次数组,查找其对应的索引。
这里我们使用底层哈希实现的set,即unordered_set
。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> result;
//将所有元素放入集合中,注意unordered_set的创建方式
unordered_set<int> hs(nums.begin(),nums.end());
for(int i=0;i<nums.size();++i){
int number=target-nums[i];
if(hs.find(number)!=hs.end()){
//如果找到答案,才会进行索引查找
for(int j=i+1;j<nums.size();++j){
if(nums[j]==number){
result.push_back(i);
result.push_back(j);
return result;
}
}
}
}
return result;
}
};
由于哈希在添加、查找方面的时间复杂度都是 O ( 1 ) O(1) O(1),所以能够大大降低之前的时间复杂度。
运行结果:
2)使用map
前面我们使用set最后需要遍历一次数组,找到答案对应的索引。但是我们使用map就可以存储元素对应的索引,即
键——元素;
值——索引;
但是,添加到map中的元素一旦有重复的值,就会发生覆盖现象。为了解决这个问题,我们不从一开始就将所有元素放入查找表中,而是每次将查找元素V的前面的元素放入查找表中。
如果在V(红色)前面有相同的元素等于V,此时待查找的另一个元素target-V如果在前面的数组中(蓝色部分),查找表中也不会有重复元素。如果没有,就将V(红色)添加进查找表中。
继续向后查找元素,就算前面的查找表有了重复元素,那也不会是我们要寻找的答案。因为,假设下一个元素是我们要找的答案,那个另一个元素如果是前面查找表中的那个重复元素,结果就会有3个元素,这与题目中两个元素相悖。
因此,采用局部放入查找表的方法可行。
实现:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> record;
for(int i=0;i<nums.size();++i){
int complement=target-nums[i];
if(record.find(complement)!=record.end()){
//如果找到,就将索引放入数组中
int res[2]={i,record[complement]};
//这里返回直接将数组放入容器中,res+2表示最后一个元素的后一个位置
return vector<int>(res,res+2);
}
record[nums[i]]=i;
}
throw invalid_argument("the input has no solution");
}
};