四数之和,排序+双指针
题目描述:
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
- 0 <= a, b, c, d < n
- a、b、c 和 d 互不相同
- nums[a] + nums[b] + nums[c] + nums[d] == target
解题思路:
思路一:
- 如果采用暴力循环,那么需要四重循环,时间复杂度为O(n4),不是很理想。
- 由于最终要得到不重复的四元组,因此我们要思考如何去重。最简单的去重办法,当然是将结果保存在一个哈希表中,使用哈希表去重。但是这种去重操作,时间复杂度和空间复杂度都不是很好。
思路二:
如果引入排序,那该问题在去重和提高时间复杂度上,都会有一个更好的解决思路。
-
首先,对元素进行排序。之后,为了保证不遍历到重复的元素,我们需要保证两点:
(1)我们可以在每重循环的时候,判断nums[i] == nums[i+1]是否成立,如果成立,则执行自增操作。
(2)每重循环遍历的下标,都要大于上一重循环。
因为排序之后,这样做就既保证了每一重循环枚举到的元素不小于其上一重循环枚举到的元素,并且在同一重循环中不能多次枚举到相同的元素。 -
由于对元素进行了排序,所有最后两趟二重循环,我们就可以简化为双指针进行操作
-
为了进一步提高时间效率,我们可以进行剪枝操作。
if ((long) nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
break;
}
if ((long) nums[i] + nums[length - 3] + nums[length - 2] + nums[length - 1] < target) {
continue;
}
由于题目中数据的范围4个相加可能超过int的取值范围,所以需要把这个临时结果转化为long类型。
vector<vector<int>> fourSum(vector<int>& nums, int target) {
//先排序,再使用双重循环和双指针
//排序是很关键的一步,它可以保证后续的查找,不会出现重复元素,也有利于使用双指针。
sort(nums.begin(),nums.end());
vector<vector<int>> res;
int n = nums.size();
for(int i = 0; i < n-3; i++){
if(i > 0 && nums[i] == nums[i-1]){
continue;
}
if ((long) nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
break;
}
if ((long) nums[i] + nums[n - 3] + nums[n - 2] + nums[n - 1] < target) {
continue;
}
for(int j = i+1; j < n-2; j++){
if(j > i+1 && nums[j] == nums[j-1]){
continue;
}
//左指针直接从j+1开始,否则会重复
int lp = j+1;
int rp = n-1;
while(lp < rp){
long t = (long)target - nums[i] - nums[j];
if(nums[lp] + nums[rp] == t){
res.push_back({nums[i],nums[j],nums[lp],nums[rp]});
while(lp < rp && nums[lp] == nums[lp+1]){
lp++;
}
lp++;
while(lp < rp && nums[rp] == nums[rp-1]){
rp--;
}
rp--;
}else if(nums[lp] + nums[rp] < t){
lp++;
}else{
rp--;
}
}
}
}
return res;
}