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实现,那么处理的就更加麻烦了。

综合来说,我建议使用第一种方法,建议循环实现。

建议和这一道题leetcode 454. 4Sum II 一起学习

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();
            }
        }
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值