1. 程式人生 > >leetcode 18. 4Sum KSum的解決辦法

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

。 這個問題的比較麻煩的地方是要注意處理重複的元素,因為index可能出現重複,index對應的value重複問題,可以在程式碼中看出,這個處理比較麻煩。這僅僅是4Sum就這麼麻煩,要是針對KSum,要是使用Hash實現,那麼處理的就更加麻煩了。

綜合來說,我建議使用第一種方法,建議迴圈實現。

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();
            }
        }
    }
};