给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
1.Java
排序+双指针
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
int n = nums.length;
Arrays.sort(nums);
List<List<Integer>> ans = new ArrayList<List<Integer>>();
// 枚举 a
for (int first = 0; first < n; ++first) {
// 需要和上一次枚举的数不相同
if (first > 0 && nums[first] == nums[first - 1]) {
continue;
}
// c 对应的指针初始指向数组的最右端
int third = n - 1;
int target = -nums[first];
// 枚举 b
for (int second = first + 1; second < n; ++second) {
// 需要和上一次枚举的数不相同
if (second > first + 1 && nums[second] == nums[second - 1]) {
continue;
}
// 需要保证 b 的指针在 c 的指针的左侧
while (second < third && nums[second] + nums[third] > target) {
--third;
}
// 如果指针重合,随着 b 后续的增加
// 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
if (second == third) {
break;
}
if (nums[second] + nums[third] == target) {
List<Integer> list = new ArrayList<Integer>();
list.add(nums[first]);
list.add(nums[second]);
list.add(nums[third]);
ans.add(list);
}
}
}
return ans;
}
}
2.C#
法1:双指针
public IList<IList<int>> ThreeSum(int[] nums)
{
IList<IList<int>> result = new List<IList<int>>();
int len = nums.Length;
if (len < 3) return result;
Array.Sort(nums);
for (int i = 0; i < len - 2; i++)
{
if (nums[i] > 0) break;
if (i > 0 && nums[i] == nums[i - 1]) continue; // 去重
int left = i + 1;
int right = len - 1;
while (left < right)
{
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0)
{
result.Add(new List<int>() { nums[i], nums[left], nums[right] });
while (left < right && nums[left] == nums[left + 1]) left++; // 去重
while (left < right && nums[right] == nums[right - 1]) right--; // 去重
left++;
right--;
}
else if (sum < 0) left++;
else if (sum > 0) right--;
}
}
return result;
}
法2:哈希表
public IList<IList<int>> ThreeSum(int[] nums)
{
int len = nums.Length;
var result = new List<IList<int>>();
if (len < 3) return result;
Array.Sort(nums);
//map<值,lastIndex>
var map = new Dictionary<int, int>();
for (int i = 0; i < len; i++)
{
if (map.ContainsKey(nums[i])) map[nums[i]] = i;
else map.Add(nums[i], i);
}
for (int i = 0; i < len - 2; i++)
{
if (nums[i] > 0) break;
if (i > 0 && nums[i] == nums[i - 1]) continue;
for (int j = i + 1; j < len - 1; j++)
{
if (j != i + 1 && nums[j] == nums[j - 1]) continue;
int numsK = 0 - nums[i] - nums[j];
if (map.ContainsKey(numsK) && map[numsK] > j)
result.Add(new List<int>() { nums[i], nums[j], numsK });
}
}
return result;
}
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:
答案中不可以包含重复的四元组。
示例:
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
满足要求的四元组集合为:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
1.java
public List<List<Integer>> fourSum(int[] nums,int target){
/*定义一个返回值*/
List<List<Integer>> result=new ArrayList<>();
/*当数组为null或元素小于4个时,直接返回*/
if(nums==null||nums.length<4){
return result;
}
/*对数组进行从小到大排序*/
Arrays.sort(nums);
/*数组长度*/
int length=nums.length;
/*定义4个指针k,i,j,h k从0开始遍历,i从k+1开始遍历,留下j和h,j指向i+1,h指向数组最大值*/
for(int k=0;k<length-3;k++){
/*当k的值与前面的值相等时忽略*/
if(k>0&&nums[k]==nums[k-1]){
continue;
}
/*获取当前最小值,如果最小值比目标值大,说明后面越来越大的值根本没戏*/
int min1=nums[k]+nums[k+1]+nums[k+2]+nums[k+3];
if(min1>target){
break;
}
/*获取当前最大值,如果最大值比目标值小,说明后面越来越小的值根本没戏,忽略*/
int max1=nums[k]+nums[length-1]+nums[length-2]+nums[length-3];
if(max1<target){
continue;
}
/*第二层循环i,初始值指向k+1*/
for(int i=k+1;i<length-2;i++){
/*当i的值与前面的值相等时忽略*/
if(i>k+1&&nums[i]==nums[i-1]){
continue;
}
/*定义指针j指向i+1*/
int j=i+1;
/*定义指针h指向数组末尾*/
int h=length-1;
/*获取当前最小值,如果最小值比目标值大,说明后面越来越大的值根本没戏,忽略*/
int min=nums[k]+nums[i]+nums[j]+nums[j+1];
if(min>target){
continue;
}
/*获取当前最大值,如果最大值比目标值小,说明后面越来越小的值根本没戏,忽略*/
int max=nums[k]+nums[i]+nums[h]+nums[h-1];
if(max<target){
continue;
}
/*开始j指针和h指针的表演,计算当前和,如果等于目标值,j++并去重,h--并去重,当当前和大于目标值时h--,当当前和小于目标值时j++*/
while (j<h){
int curr=nums[k]+nums[i]+nums[j]+nums[h];
if(curr==target){
result.add(Arrays.asList(nums[k],nums[i],nums[j],nums[h]));
j++;
while(j<h&&nums[j]==nums[j-1]){
j++;
}
h--;
while(j<h&&i<h&&nums[h]==nums[h+1]){
h--;
}
}else if(curr>target){
h--;
}else {
j++;
}
}
}
}
return result;
}
2.C#。四数之和其实就是在三数之和题目的基础上再多做一层循环嵌套,本质的算法思想是一致,都是采用双指针的解法。
测试用例:nums:[1, 0, -1, 0, -2, 2],target:0
先对nums做一个升序的排序得到[-2,-1,0,0,1,2]
利用两层循环首先是:i=-2,j=-1,然后剩下的区间为:[0,0,1,2],利用双指针进行遍历。即有:i=-2,j=-1,L=0,R=2,四数相加等于:-1,不等于且小于target,左指针加一位,即:L++,否则右指针减一位:R–。若等于target,就存储在结果中,并对左右指针相邻重复的数做去重操作,继续上面的步骤
public class Solution {
public IList<IList<int>> FourSum(int[] nums, int target) {
IList<IList<int>> resultList=new List<IList<int>>();
int Length=nums.Length;
if(nums==null||Length<4) return resultList;
Array.Sort(nums);
for(int i=0;i<Length;i++){
if(i>0&&nums[i]==nums[i-1]) continue;//去重
for(int j=i+1;j<Length;j++){
if(j>i+1&&nums[j]==nums[j-1]) continue;//去重
int L=j+1;
int R=Length-1;
while(L<R){
int sum=nums[i]+nums[j] + nums[L] + nums[R];
if(sum==target){
resultList.Add(new List<int>{nums[i],nums[j],nums[L],nums[R]});
while(L<R&&nums[L]==nums[L+1]) L++;//去重
while(L<R&&nums[R]==nums[R-1]) R--;//去重
L++;
R--;
}
else if(sum>target) R--;
else if(sum<target) L++;
}
}
}
return resultList;
}
}