两数之和(easy)
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
方法一:暴力求解
很自然的方法就是依次遍历数组中每个数,在剩下的数中找是否存在和为target的数,返回他们的下标。代码如下:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> ret;
ret.clear();
if(nums.size() == 0){
return ret;
}
for(int i=0; i<nums.size()-1; ++i){
for(int j=i+1; j<nums.size(); ++j){
if(nums[i] + nums[j] == target){
ret.push_back(i);
ret.push_back(j);
return ret;
}
}
}
return ret;
}
};
时间复杂度O(n^2)
方法二:hash表
利用一个哈希表,通过O(n)的空间消耗,将时间复杂度降低到O(n)。
哈希表的key存放数组的值,value是对应的下标。
遍历数组,在哈希表中查找是否存在和为target的数,不存在就将当前数加入哈希表,存在就返回value值(即数组中的下标)
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> ret;
ret.clear();
unordered_map<int, int> hash;
for(int i=0; i <nums.size(); ++i){
int findValue = target-nums[i];
if(hash.find(findValue) != hash.end()){
ret.push_back(i);
ret.push_back(hash[findValue]);
return ret;
}else{
hash[nums[i]] = i;
}
}
return ret;
}
};
关键是对unordered_map这个类型的熟悉使用。
时间复杂度降低了很大
三数之和(medium)
在上面的问题基础上引入三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
很自然的也想到用遍历的方法去寻找,自然时间复杂度是O(n^3),但是要求不能包含重复的三元组,这个在判断上就增加了复杂程度。
看到“避免重复”,可以想到将数组先进行排序,这样在遍历的时候,可以通过比较前后两个值的大小,跳过对重复数字的判断。
方法一:排序后暴力法
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int n = nums.size();
sort(nums.begin(), nums.end());
vector<vector<int>> result;
result.clear();
for(int i = 0; i<n-1; ++i){
if(i>0 && nums[i]==nums[i-1]){
continue;
}
for(int j=i+1; j<n-1; ++j){
if(j>i+1 && nums[j]==nums[j-1]){
continue;
}
for(int k=j+1; k<n; ++k){
if(k>j+1 && nums[k]==nums[k-1]){
continue;
}
if(nums[i]+nums[j]+nums[k] ==0){
result.push_back({nums[i], nums[j], nums[k]});
}
}
}
}
return result;
}
};
通过nums[j]==nums[j-1]来跳过对重复数字的遍历,但时间复杂度O(n^3),一般不满足要求。
方法二:排序后双指针法
排序后数字是以从小到大的顺序排序,那么在n[i]确定的情况下,如果n[j]+n[k]> -n[i],那么n[j]+n[k+1]肯定是大于-n[i]的,k只能朝数组左边移动。所以在从左到右遍历第二个数的时候,同时从右到左遍历第三个数,将两重遍历合并为一重遍历。同时要注意边界条件是j=k。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int n = nums.size();
sort(nums.begin(), nums.end());
vector<vector<int>> result;
result.clear();
for(int i = 0; i<n-1; ++i){
if(i>0 && nums[i]==nums[i-1]){
continue;
}
int k = n-1;
int target = -nums[i];
for(int j=i+1; j<n-1; ++j){
if(j>i+1 && nums[j]==nums[j-1]){
continue;
}
while(j<k && nums[j]+nums[k]> target){
--k;
}
if(j == k){
break;
}
if(nums[j]+nums[k] == target){
result.push_back({nums[i], nums[j], nums[k]});
}
}
}
return result;
}
};
时间复杂度:排序O(nlogn)+ 遍历O(n^2),一共O(n^2)。