Two Sum
未排序的数组,输出其中a+b=target的a和b的下标(只需输出一对即可)
思路:需要输出下标,故不能直接排序然后2sum,否则下标会有问题
使用空间换取时间,即使用Hashmap来建立数字和其坐标位置之间的映射,我们都知道HashMap是常数级的查找效率,这样,我们在遍历数组的时候,用target减去遍历到的数字,就是另一个需要的数字了,直接在HashMap中查找其是否存在即可,注意要判断查找到的数字不是第一个数字,比如target是4,遍历到了一个2,那么另外一个2不能是之前那个2
复杂度分析
- 时间复杂度:O(n)。
- 空间复杂度:O(n)
(1)两遍哈希表
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> res;
if(nums.size() < 2) return res;
// 空间换时间
map<int,int> m;
for(int i = 0;i < nums.size();i++){
m[nums[i]] = i;
}
// 确定一个数,找另外一个数是否在字典中
for(int i = 0;i < nums.size();i++){
int t = target-nums[i];
// 注意m[t] != i,不能是自身!
if(m.count(t) && m[t] != i){
res.push_back(i);
res.push_back(m[t]);
break;
}
}
return res;
}
};
(2)一遍哈希表
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> res;
if(nums.size() < 2) return res;
// 直接边插入边查找,效率更高
map<int,int> m;
for(int i = 0;i < nums.size();i++){
int t = target-nums[i];
if(m.count(t) && m[t] != i){
res.push_back(i);
res.push_back(m[t]);
break;
}
m[nums[i]] = i;
}
return res;
}
};
3Sum
Given an array nums
of n integers, are there elements a, b, c in nums
such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note:
The solution set must not contain duplicate triplets.
Example:
Given array nums = [-1, 0, 1, 2, -1, -4],
A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]
思路:主要注意的点就是去重问题
时间复杂度:O(N*N)
class Solution {
public:
vector<vector<int>> res;
vector<vector<int>> threeSum(vector<int>& nums) {
if(nums.size() < 3) return res;
sort(nums.begin(),nums.end());
for(int i = 0;i < nums.size();i++){
int l = i+1,r = nums.size()-1,sum;
while(l < r){
sum = nums[l]+nums[r]+nums[i];
if(sum == 0){
vector<int> out = {nums[i],nums[l],nums[r]};
res.push_back(out);
// 去重
while(l < r && nums[l+1] == nums[l]) l++;
while(l < r && nums[r-1] == nums[r]) r--;
l++;
r--;
}
else if(sum < 0) l++;
else r--;
}
// 去重
while(i < nums.size()-1 && nums[i+1] == nums[i]) i++;
}
return res;
}
};
3Sum Closest
Given an array nums
of n integers and an integer target
, find three integers in nums
such that the sum is closest to target
. Return the sum of the three integers. You may assume that each input would have exactly one solution.
Example:
Given array nums = [-1, 2, 1, -4], and target = 1.
The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).
思路:不必考虑去重,相对简单,只需增加判断是否最近即可
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
int closest = nums[0] + nums[1] + nums[2];
int diff = abs(closest - target);
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size() - 2; ++i) {
int left = i + 1, right = nums.size() - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
int newDiff = abs(sum - target);
if (diff > newDiff) {
diff = newDiff;
closest = sum;
}
if (sum == target) return target;
else if (sum < target) ++left;
else --right;
}
}
return closest;
}
};
4Sum
Given an array nums
of n integers and an integer target
, are there elements a, b, c, and d in nums
such that a + b + c + d = target
? Find all unique quadruplets in the array which gives the sum of target
.
Note:
The solution set must not contain duplicate quadruplets.
Example:
Given array nums = [1, 0, -1, 0, -2, 2], and target = 0.
A solution set is:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
主要注意去重问题
class Solution {
public:
vector<vector<int>> res;
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());
vector<int> out;
ksum(nums,out,target,4,0,nums.size()-1);
return res;
}
void ksum(vector<int>& nums,vector<int> out,int target,int k, int l,int r){
if(k < 2) return;
if(k == 2){
while(l < r){
int sum = nums[l]+nums[r];
if(sum == target){
out.push_back(nums[l]);
out.push_back(nums[r]);
res.push_back(out);
// 去重
while(l < r && nums[l] == nums[l+1]) l++;
while(l < r && nums[r] == nums[r-1]) r--;
l++;r--;
out.pop_back();
out.pop_back();
}
else if(sum < target) l++;
else r--;
}
}
else{
for(int i = l;i <= r-k+1;i++){
out.push_back(nums[i]);
ksum(nums,out,target-nums[i],k-1,i+1,r);
out.pop_back();
// 去重
while(i < r && nums[i] == nums[i+1]) i++;
}
}
}
};
关于Ksum
2 sum 用hash table做,可以时间O(n),空间O(n),
2 sum 如果用sort以后,在前后扫描,可以时间O(nlogn + n) = O(nlogn),空间O(1)
2 sum 用hash table做的好处是快,但是等于是利用了不用排序的特点。排序的办法,在高维度(也就是k sum问题,k>2)的时候,nlogn就不是主要的时间消耗成分,也就更适合2sum的sort后双指针扫描查找的办法。
那么,对于k sum, k>2的,如果用sort的话, 可以 对 n-2的数做嵌套循环,因为已经sort过了,最后剩下的两维用2 sum的第二个办法, 时间是O(nlogn + n^(k-2) * n) = O(n^(n-1)),空间O(1)。 但是这样跟纯嵌套循环没有什么区别,只是最后一层少了一个因子n。有什么办法能优化?
就是说,对于 k sum (k>2) 问题 (一个size为n的array, 查找k个数的一个tuple,满足总和sum为0), 有没有时间复杂度在O(n^(k-2))的办法?
之前常规的一层一层剥离,n的次数是递增的。只有在最后一层,还有两个维度的时候,时间开销上减少一个n的因子,但是这样时间开销还是太多
我们可以通过对问题分解来解决
举个例子
...-5,-4,-3,-2,-1, 0,1, 2, 3, 4, 5.... 要找 4 sum = 0
那么先分解
4 分成 2 sum + 2 sum 来解决,但是这里的子问题2 sum没有sum=0的要求,是保留任何中间值。只有当子问题的2 sum解决以后,回归原问题的时候,我们才又回归原始的2 sum问题,这时候sum=0
子问题,空间和时间消耗,都是O(n^2)
回归大问题,时间消耗,是O(n^2)
假设k sum中 k = 2^m, 那么一共有m层,会有m次分解
分解到最底层,时间空间消耗 从 原始O(n)变为新的O(n^2)
分解到次底层,时间空间消耗 从 O(n^2)变为新的O((n^2)^2)
...
到达最顶层,时间空间消耗就都变成了O(n^(2*m)) = O(n^(2logk))
和之前的方法O(n^(k-1))相比,O(n^(2logk))的时间是少了很多,但是空间消耗却很大。
因为子问题无法确定把哪一个中间结果留下,那么就需要把子问题的结果全部返回,到最后,空间消耗就很大了。整体效果算是空间换时间吧。
通过 问题的分解 + hashtable的运用,能明显减少时间消耗, 但是空间消耗变大是个问题。比如说,如果有10^6的int类型数组,我如果用这个hashtable的办法,就要有10^12的pair,这就有10T以上的空间消耗。
问题的分解是个很好的思路,但是中间值得保留迫使空间消耗增大,这和用不用hashtable倒没有很大关系,只是说,如果不用hashtable,时间消耗会更大。