给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
做题可以直接到两数之和
输入
nums = [2, 7, 11, 15], target = 9
输出
[0, 1]
解释
nums[0] + nums[1] = 2 + 7 = 9
依然给出两份代码,一份是本地可运行的,一份是提交的。
因为题目中没有说数组有序,所以应该是无序数组,首先想到的就是暴力解法。遍历数组中的任意两组和,直到找到一个与目标值匹配的组合,返回结果。
这种解决方案显然并不好,时间复杂度O(n^2),很高。
#include
#include
using namespace std;
int n, nums[20000];
int target;
vector<int> twoSum(){
vector<int> v;
int p1 = 0;
while(p1<n){
int p2 = p1+1;
while(p2<n){
if(nums[p1] + nums[p2] == target){
v.push_back(p1);
v.push_back(p2);
return v;
}
p2++;
}
p1++;
}
}
int main(){
scanf("%d", &n);
for(int a=0; a<n; a++)
scanf("%d", &nums[a]);
scanf("%d", &target);
vector<int> res = twoSum();
printf("%d %d", res[0], res[1]);
return 0;
}
vector<int> twoSum(vector<int>& nums, int target) {
int n = nums.size();
vector<int> v;
int p1 = 0;
bool isfind = false;
while(p1<n){
int p2 = p1+1;
while(p2<n){
if(nums[p1] + nums[p2] == target){
v.push_back(p1);
v.push_back(p2);
isfind = true;
}
if(isfind) break;
p2++;
}
if(isfind) break;
p1++;
}
return v;
}
开始提交时提交代码与本地代码一致,都是发现正解直接返回,结果提交编译不通过,报错
Char 5: error: control reaches end of non-void function [-Werror=return-type]
编译器认为这种写法可能存在某些情况得不到返回结果。于是改成了上文的形式,在代码段的结束位置返回结果向量v。
暴力搜索太慢了。昨天刚学了“双指针法”,该方法的其中一种适用场景就是,对于一个有序数组,找到两个数的和等于特定值。
本题中给的是无序数组,要想变成有序数组,需要先排序并且记录排序后的每个元素对应的原始下标,再用双指针法寻找加和等于目标值的两个元素,并返回结果。
快速排序的时间复杂度是O(nlogn),双指针法寻找结果的时间复杂度也是O(nlogn)。
#include
#include
#include
using namespace std;
int n, nums[20000];
int target;
struct num{
int value;
int index;
num(){
value = 0;
index = 0;
}
num(int a, int b){
value = a;
index = b;
}
};
num in[20000];
bool operator<(num a, num b){
if(a.value < b.value) return true;
return false;
}
vector<int> twoSum(){
vector<int> v;
for(int a=0; a<n; a++)
in[a] = num(nums[a], a);
sort(in, in+n);
int p1=0, p2=n-1;
while(p1<p2){
if(in[p1].value + in[p2].value == target){
v.push_back(in[p1].index);
v.push_back(in[p2].index);
break;
}
else if(in[p1].value + in[p2].value < target)
p1++;
else p2--;
}
return v;
}
int main(){
scanf("%d", &n);
for(int a=0; a<n; a++)
scanf("%d", &nums[a]);
scanf("%d", &target);
vector<int> res = twoSum();
printf("%d %d", res[0], res[1]);
return 0;
}
struct num{
int value;
int index;
num(){
value = 0;
index = 0;
}
num(int a, int b){
value = a;
index = b;
}
};
num in[20000];
bool operator<(num a, num b){
if(a.value < b.value) return true;
return false;
}
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> v;
int n = nums.size();
for(int a=0; a<n; a++)
in[a] = num(nums[a], a);
sort(in, in+n);
int p1=0, p2=n-1;
while(p1<p2){
if(in[p1].value + in[p2].value == target){
v.push_back(in[p1].index);
v.push_back(in[p2].index);
break;
}
else if(in[p1].value + in[p2].value < target)
p1++;
else p2--;
}
return v;
}
};
刚开始提交的时候把我自己写的所有内容都放在class内了,结果编译不通过,报错
error: 'bool operator< must have exactly one argument
原因是类内的运算符重载,只接受一个参数,是将传入参数与类试题进行运算。而我需要的是两个结构体之间的运算,所有把结构体定义和运算符重载移到类外,就能通过了。
看别人的题解,很多人提到通过哈希表解决,时间复杂度能达到O(n),太强了。
遍历数组nums中的元素,在hash表的第nums[i]的位置存入下标i,并查找目标值减当前值是否已经在哈希表中了。如果在则返回对应的下标和当前下标。
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> hash;
for(int i = 0; i < nums.size(); i++){
if(hash.count(target - nums[i])) return {hash[target - nums[i]], i};
hash[nums[i]] = i;
}
return {-1, -1};
}
【注】题目中提到不能重复使用同一个元素,即如果给定数组是[2, 4, 6],目标值是8,应该是2+6得到的,而不能是4+4得到的。上述写法其实可以保证元素不重复使用。因为先检查目标值-当前值是否在hash表中,找不到的话再将当前值存入hash表。如果能找到的肯定不当前值(因为还没有存)。
参考两数之和评论区
两数之和官方题解