leetcode 18. 4Sum KSum的解決辦法
Given an array S of n integers, are there elements a, b, c, and d in S 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.
For example, given array S = [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]
]
題意很簡單,這裡就不多說了。這裡做一個總結:
第一種方法是把KSum降低為K-1Sum,然後逐步降低,最後處理2Sum,這個可以通過遞迴實現,也可以通過迴圈實現,最後的2Sum是通過雙指標遍歷得到,但是要注意避免重複元素的計算。這裡有一個小細節需要注意:我們要尋找KSum的解,那麼陣列遍歷範圍是[0,len-k+1)或者[0,len-k],所以在降低k值的時候要注意遍歷範圍的計算;
第二種是通過Hash實現,不過這個是針對4Sum的,針對KSum也可以類似處理,不過實現起來有點麻煩。思想主要就是把可能的2Sum之和存放到map中,然後通過雙指標遍歷map來實現2Sum+2Sum=4Sum
綜合來說,我建議使用第一種方法,建議迴圈實現。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Solution
{
/*
* 通過吧KSum降低為K-1Sum,然後逐步降低,最後處理2Sum
*
* */
public List<List<Integer>> fourSumBBBB(int[] nums, int target)
{
Arrays.sort(nums);
return kSum(nums, target, 3);
}
public List<List<Integer>> kSum(int[] nums, int target,int kSum)
{
List<Integer> one=new ArrayList<>();
kSumByDG(kSum, nums, 0, nums.length-kSum+1, target, one);
return finResList;
}
public void kSumByDG(int kSum,int[] nums,int beg ,int end,int target,List<Integer> one)
{
if(kSum==2)
{
int allSum=0;
for(int i=0;i<one.size();i++)
allSum+=one.get(i);
int left=beg,right=end;
while(left < right)
{
int ssum = allSum + nums[left]+nums[right];
if(ssum==target)
{
List<Integer> tmp = new ArrayList<>(one);
tmp.add(nums[left]);
tmp.add(nums[right]);
finResList.add(tmp);
while(left < right && nums[left]==nums[left+1]) left++;
while(left < right && nums[right]==nums[right-1]) right--;
left++;
right--;
}else if(ssum < target)
left++;
else
right--;
}
return ;
}else
{
for(int i=beg;i<end;i++)
{
while(i!=beg && nums[i]==nums[i-1])
continue;
if( i+1 < end+1)
{
one.add(nums[i]);
kSumByDG(kSum-1, nums, i+1, end+1, target, one);
one.remove(one.size()-1);
}
}
}
}
/*
* 通過迴圈,來實現,和上面通過遞迴實現的本質是一樣的。
* */
public List<List<Integer>> fourSum(int[] nums, int target)
{
List<List<Integer>> finResList=new ArrayList<>();
if(nums==null ||nums.length<4)
return finResList;
Arrays.sort(nums);
for(int i=0;i<nums.length-3;i++)
{
if(i>0 && nums[i]==nums[i-1]) continue;
for(int j=i+1;j<nums.length-2;j++)
{
if(j!=i+1 && nums[j]==nums[j-1]) continue;
int beg=j+1;
int end=nums.length-1;
while(beg < end)
{
int sum = nums[i] + nums[j] + nums[beg] + nums[end];
if(sum==target)
{
List<Integer> t = new ArrayList<Integer>();
t.add(nums[i]);
t.add(nums[j]);
t.add(nums[beg]);
t.add(nums[end]);
finResList.add(t);
while(beg<end && nums[beg+1]==nums[beg])
beg++;
while(beg<end && nums[end-1]==nums[end])
end--;
beg++;
end--;
}else if(sum < target)
beg++;
else
end--;
}
}
}
return finResList;
}
HashMap<Integer, List<List<Integer>>> map=new HashMap<>();
List<List<Integer>> finResList=new ArrayList<>();
List<List<Integer>> finIndexList=new ArrayList<>();
//使用hash需靠考慮的細節實在是太多了,下面的方法並不能覆蓋所有情況
public List<List<Integer>> fourSumWithHash(int[] nums, int target)
{
Arrays.sort(nums);
if(nums==null || nums.length<4)
return finResList;
else if(nums.length==4)
{
int sum=0;
List<Integer> tt=new ArrayList<>();
for(int i=0;i<nums.length;i++)
{
tt.add(nums[i]);
sum+=nums[i];
}
if(sum==target)
finResList.add(tt);
return finResList;
}
for(int i=0;i<nums.length;i++)
{
for (int j = i+1; j < nums.length; j++)
{
int sum=nums[i] + nums[j];
List<Integer> tmp=new ArrayList<>();
tmp.add(i);
tmp.add(j);
tmp.add(nums[i]);
tmp.add(nums[j]);
List<List<Integer>> res=map.get(sum);
if(res==null)
{
res=new ArrayList<>();
map.put(sum, res);
}
map.get(sum).add(tmp);
}
}
List<Integer> key=new ArrayList<>(map.keySet());
key.sort(null);
int left=0,right=key.size()-1;
while(left <= right)
{
int sum=key.get(left) + key.get(right);
if(sum==target)
{
List<List<Integer>> res1=map.get(key.get(left));
List<List<Integer>> res2=map.get(key.get(right));
for (int j = 0; j < res1.size(); j++)
{
for (int k = 0; k < res2.size(); k++)
{
AddOne(res1.get(j),res2.get(k));
}
}
left++;
right--;
}else if(sum < target)
left++;
else
right--;
}
return finResList;
}
public void AddOne(List<Integer> a,List<Integer> b)
{
Set<Integer> set=new HashSet<Integer>();
set.add(a.get(0));
set.add(a.get(1));
set.add(b.get(0));
set.add(b.get(1));
if(set.size()<4)
return ;
List<Integer> index=new ArrayList<>(set);
index.sort(null);
boolean need=true;
/*
* 重複新增問題的解決,先判斷index的重複
* */
for(int i=0;i<finIndexList.size();i++)
{
List<Integer> tt=finIndexList.get(i);
if(tt.get(0)==index.get(0) && tt.get(1)==index.get(1) && tt.get(2)==index.get(2) && tt.get(3)==index.get(3))
{
need=false;
break;
}
}
if(need)
{
List<Integer> res=new ArrayList<>();
res.add(a.get(2));
res.add(a.get(3));
res.add(b.get(2));
res.add(b.get(3));
res.sort(null);
boolean sub=true;
//index不重複的前提下考慮,具體數值的重複
for(int i=0;i<finResList.size();i++)
{
List<Integer> tt=finResList.get(i);
if(tt.get(0)==res.get(0) && tt.get(1)==res.get(1) && tt.get(2)==res.get(2) && tt.get(3)==res.get(3))
{
sub=false;
break;
}
}
if(sub)
finResList.add(res);
}
}
public static void main(String[] args)
{
Solution so=new Solution();
int []nums={-2,-1,0,0,1,2};
System.out.println(so.fourSum(nums, 0));
}
}
下面是KSum的C++實現,主要是通過把K Sum 降低為k-1 Sum來做的,就是一個遞迴解決問題。
程式碼如下:
#include <iostream>
#include <vector>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <queue>
#include <stack>
#include <string>
#include <climits>
#include <algorithm>
#include <sstream>
#include <functional>
#include <bitset>
#include <numeric>
#include <cmath>
#include <regex>
using namespace std;
class Solution
{
public:
vector<vector<int>> res;
vector<vector<int>> fourSum(vector<int>& nums, int target)
{
sort(nums.begin(), nums.end());
calaKSum(nums, target, 4);
return res;
}
void calaKSum(vector<int>& a, int tar,int k)
{
vector<int> one;
dfs(a, tar, k, one, 0, a.size() - k + 1);
}
void dfs(vector<int>& a, int tar, int k, vector<int>& one, int beg, int end)
{
if (k == 2)
{
int sum = accumulate(one.begin(), one.end(), 0);
int left = beg, right = end;
while (left < right)
{
int tmp = sum + a[left] + a[right];
if (tmp == tar)
{
vector<int> tmp = one;
tmp.push_back(a[left]);
tmp.push_back(a[right]);
res.push_back(tmp);
while (left < right && a[left] == a[left + 1]) left++;
while (left < right && a[right] == a[right - 1]) right--;
left++;
right--;
}
else if (tmp < tar)
left++;
else
right--;
}
}
else
{
for (int i = beg; i < end; i++)
{
if (i != beg && a[i] == a[i - 1])
continue;
one.push_back(a[i]);
dfs(a, tar, k-1, one, i + 1, end+1);
one.pop_back();
}
}
}
};