0918-2019-LEETCODE_18_四数之和扩展至K数之和

四数之和:可以转化成三数之和,再转化成两数之和。每加一个数可以多加一个for循环,记得去重和特判,因为是排序数组,所以可以加特判,判断当前的选择中最小值是否大于 target,大于的话就是返回到下一次循环继续判断。如果当前的选择的最大值,小于 target,那也不用循环了,直接返回到下一次循环继续判断。
代码来源:
https://leetcode-cn.com/problems/4sum/solution/ji-bai-9994de-yong-hu-you-dai-ma-you-zhu-shi-by-yo/

	public List<List<Integer>> fourSum(int[] nums, int target) {
        ArrayList<List<Integer>> res = new ArrayList<>();
        if (nums == null || nums.length < 4) return res;
        Arrays.sort(nums);
        int len = nums.length;
        for (int i = 0; i < len - 3; i++) {
            if (i > 0 && nums[i] == nums[i - 1]) continue;
            int temp = target - nums[i];
            if (nums[i + 1] + nums[i + 2] + nums[i + 3] > temp) continue;
            if (nums[len - 1] + nums[len - 2] + nums[len - 3] < temp) continue;
            for (int j = i + 1; j < len - 2; j++) {
                if (nums[j] + nums[j + 1] + nums[j + 2] > temp) continue;
                if (nums[j] + nums[len - 1] + nums[len - 2] < temp) continue;
                if (j > i + 1 && nums[j] == nums[j - 1]) continue;
                int left = j + 1;
                int right = len - 1;
                while (left < right){
                    int sum = nums[left] + nums[right] + nums[j];
                    if (sum == temp) {
                        res.add(Arrays.asList(nums[i],nums[j],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 > temp) {
                        right--;
                    } else {
                        left++;
                    }
                }
            }
        }
        return res;
    }

上古的解答方案,自己学会了,再一次性写出来还是有些困难,但这个方法有通用性。
使用栈,需要单步运行看一下结果

package test0919;

/**
 * @author pdzz
 * @create 2019-09-19 20:01
 */
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class K_Sum_Recursive {
    /**
     * 我是一个接口,在系统提供的他们的方法里面调用我即可
     * <p>
     * 相当加了一层包装,对外提供了一个系统可以使用的接口
     *
     * @param nums   系统给定的数组
     * @param target 系统要求的目标值
     * @return 系统要求的返回值
     */
    public List<List<Integer>> kSum(int[] nums, int target, int k) {
        // 先排序,这个是必须的。
        Arrays.sort(nums);

        // 根据模板方法的要求,将该方法需要的输入都准备好。
        int[] stack = new int[k];
        Arrays.fill(stack, 0x3f3f3f3f);
        int stack_index = -1;
        int begin = 0;
        // 递归开始
        List<List<Integer>> ans = K_Sum_Recursive_Template(nums, stack, stack_index, k, begin, target);
        // 递归结束,返回解
        return ans;
    }

    /**
     * K数之和问题的模板方法,所有K数问题都可以调用这个方法
     *
     * @param nums        输入的数组
     * @param stack       定义的一个长度为 k_sum 问题中的 k 的数组,初始化为0x3f3f3f3f
     * @param stack_index 栈指针,初始化值为-1
     * @param k           表明当前问题被 分解/递归 成了 k数之和 的问题
     * @param begin       当前问题要固定的值的起点
     * @param target      当前 k数之和 的目标和
     * @return 当前 k数之和 的解集,要在上一层合并到最终解集里去
     */
    private List<List<Integer>> K_Sum_Recursive_Template(int[] nums, int[] stack, int stack_index, int k, int begin, int target) {
        List<List<Integer>> ans = new ArrayList<>();

        // 当递归到两数之和的时候,不再进行递归,直接使用左右指针法解决
        if (k == 2) {
            List<Integer> temp_ans;

            int left = begin;
            int right = nums.length - 1;

            while (left < right) {
                if (nums[left] + nums[right] > target) {
                    // 过大,因此右指针左移
                    right--;
                } else if (nums[left] + nums[right] < target) {
                    // 过小,因此左指针右移
                    left++;
                } else {
                    // 相等,加入序列中,左右指针同时向内移动一次
                    temp_ans = new ArrayList<>();
                    stack[++stack_index] = nums[left];
                    stack[++stack_index] = nums[right];

                    // 当前栈中的元素符合题目要求, 将其加入到List中去,并将该List加入到当前问题的解集中
                    for (int i = 0; i <= stack_index; i++) {
                        temp_ans.add(stack[i]);
                    }
                    ans.add(temp_ans);

                    // 栈的清理工作,其实不做也可以,因为栈指针属于形参,不会影响外面的那个栈指针,
                    // 但是还是清理掉比较好,方便调试。
                    stack[stack_index--] = 0x3f3f3f3f;
                    stack[stack_index--] = 0x3f3f3f3f;

                    left++;
                    right--;
                    while (left < right && nums[left] == nums[left - 1]) {
                        left++;
                    }
                    while (right > left && right < nums.length - 1 && nums[right] == nums[right + 1]) {
                        right--;
                    }
                }
            }
        } else {
            int target_temp;
            for (int i = begin; i < nums.length - k + 1; i++) {
                if (i > begin && nums[i] == nums[i - 1]) {
                    continue;
                }
                // 在固定一个数后,问题被降级为一个 k - 1 数之和 问题
                // 确定 k - 1 数之和 的目标和
                target_temp = target - nums[i];
                // 将当前选定的数字压入栈中,便于最后加入解集中
                stack[++stack_index] = nums[i];
                // 递归调用 k - 1 数之和 问题的求解
                List<List<Integer>> ans_temp = K_Sum_Recursive_Template(nums, stack, stack_index, k - 1, i + 1, target_temp);
                // 在选定当前数字的情况下, k - 1 数之和 问题求解完毕,
                // 将该数弹出栈,为选择下一个被选值做准备
                stack[stack_index--] = 0x3f3f3f3f;
                // 将当前解集加入当前 k数之和的解集中
                ans.addAll(ans_temp);
            }
        }
        return ans;
    }


    public static void main(String[] args) {
        K_Sum_Recursive solution = new K_Sum_Recursive();
        int[] input = {1, 0, -1, 0, -2, 2};
        int k = 4;
        int target = 0;
        System.out.println(solution.kSum(input, target, k));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值