四数之和
- 题目描述
- nSum 双指针
- 代码演示
- 上期经典
题目描述
难度 - 中等
原题链接 - 四数之和
给你一个由 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
你可以按 任意顺序 返回答案 。
示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
示例 2:
输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]
提示:
1 <= nums.length <= 200
-1e9 <= nums[i] <= 1e9
-1e9 <= target <= 1e9
nSum 双指针
nSum 问题是一类问题的总称,3个数字之和,4个数字之和,都是一类问题,用双指针解决。
想用双指针,就要保证数据是有序的,因此要先给数组排序。
这类问题的通用解法就是先确定好一个数字(三数之和)或者两个数字(四数之和)。然后用双指针,去找另外两个满足的数字。
排好序的数组,用双指针,就能保证指针不回退的找到答案,
题目里要求不能重复值,因此代码里的要做好去重。
具体实现时,还可以进行一些剪枝操作:
- 在确定第一个数之后,如果 nums[i]+nums[i+1]+nums[i+2]+nums[i+3]>target,说明此时剩下的三个数无论取什么值,四数之和一定大于 target,因此退出第一重循环;
- 在确定第一个数之后,如果 nums[i]+nums[n−3]+nums[n−2]+nums[n−1]<target,说明此时剩下的三个数无论取什么值,四数之和一定小于 target,因此第一重循环直接进入下一轮,枚举 nums[i+1];
- 在确定前两个数之后,如果 nums[i]+nums[j]+nums[j+1]+nums[j+2]>target,说明此时剩下的两个数无论取什么值,四数之和一定大于 target,因此退出第二重循环;
- 在确定前两个数之后,如果 nums[i]+nums[j]+nums[n−2]+nums[n−1]<target,说明此时剩下的两个数无论取什么值,四数之和一定小于 target,因此第二重循环直接进入下一轮,枚举 nums[j+1]。
代码演示
/*** 四数之和* @param nums* @param target* @return*/public List<List<Integer>> fourSum(int[] nums, int target) {List<List<Integer>> ans = new ArrayList<>();if (nums == null || nums.length < 4){return ans;}Arrays.sort(nums);int n = nums.length;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;}//小于target 结束当前循环,指针向右移动。if ((long)nums[i] + nums[n - 1] + nums[n - 2] + nums[n - 3] < target){continue;}//三个数字选择for (int j = i + 1;j < n - 2;j++){if (j > i + 1 && nums[j] == nums[j - 1]){continue;}if ((long)nums[j] + nums[i] + nums[j + 1] + nums[j + 2] > target){break;}if ((long)nums[j] + nums[i] + nums[n - 1] + nums[n - 2] < target){continue;}//两个数字选择int left = j + 1;int right = n - 1;while (left < right){long sum = (long) nums[i] + nums[j] + nums[left] + nums[right];if (sum == target){ans.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));//去除重复数字while (left < right && nums[right] == nums[right - 1]){right--;}right--;while (left < right && nums[left] == nums[left + 1]){left++;}left++;} else if (sum > target) {//去除重复数字while (left < right && nums[right] == nums[right - 1]){right--;}right--;} else if (sum < target) {while (left < right && nums[left] == nums[left + 1]){left++;}left++;}}}}return ans;}
上期经典
leetcode15. 三数之和