【LeetCode每日一题】[中等]18. 四数之和

【LeetCode每日一题】[中等]18. 四数之和

18. 四数之和

题目来源
算法思想:数组,回溯

题目:
在这里插入图片描述
指针法思路:

  1. 首先将数组进行排序
  2. 运用指针,nums[i] and nums[j] 是四数和中的前两位数,
  3. 剩下两位在区间中[j+1,n-1]中得到,
  4. 运用下面的技巧进行剪枝:
    在这里插入图片描述

java代码–指针法

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> quadruplets = new ArrayList<List<Integer>>();
        if (nums == null || nums.length < 4) {
            return quadruplets;
        }
        Arrays.sort(nums);//排序
        int length = nums.length;
        for (int i = 0; i < length - 3; i++) {
        	//i,确定第一个数值
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }//便于去重,同一个位置的数,不使用相同的数值
            if (nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
                break;
            }//如果当前连续4个数比target大,则说明之后不可能存在答案,剪枝
            if (nums[i] + nums[length - 3] + nums[length - 2] + nums[length - 1] < target) {
                continue;
            }//如果当前的值加上最大的三个数小于target,则当前数不可能凑成四叔和,舍弃,剪枝,后续可能存在答案,所以是continue
            //确定第二个数值
            for (int j = i + 1; j < length - 2; j++) {
                if (j > i + 1 && nums[j] == nums[j - 1]) {
                    continue;
                }//便于去重,同一个位置的数,不使用相同的数值
                if (nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) {
                    break;
                }//如果当前连续4个数比target大,则说明之后不可能存在答案,剪枝
                if (nums[i] + nums[j] + nums[length - 2] + nums[length - 1] < target) {
                    continue;
                }//如果当前的值i,j加上最大的两个数小于target,则当前数不可能凑成四叔和,舍弃,后续可能存在答案,所以是continue,继续寻找合适的j,第二个数
                //找到了合适的前两个数i,j,现在确定剩下的两个数,在区间[j+1,n-1]中
                int left = j + 1, right = length - 1;//区间左右边界
                while (left < right) {
                    int sum = nums[i] + nums[j] + nums[left] + nums[right];
                    if (sum == target) {
                        quadruplets.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));//如果是,将答案放入其中
                        while (left < right && nums[left] == nums[left + 1]) {
                            left++;
                        }//如果有左边界值相同,则跳过,去重,一层循环中,同一个位置不放入相同的数值
                        left++;//左边界,后移
                        while (left < right && nums[right] == nums[right - 1]) {
                            right--;
                        }//如果有右边界值相同,则跳过,去重,一层循环中,同一个位置不放入相同的数值
                        right--;//有边界,前移
                    } else if (sum < target) {
                        left++;//如果sum<target,则说明,当前总和太小,左边界后移
                    } else {
                        right--;//如果sum>target,则说明,当前总和太大,右边界前移
                    }
                }
            }
        }
        return quadruplets;
    }
}

java代码–回溯

分析思路.
利用回溯全排列,剪枝去重,核心剪枝方法如下:

if(start != i && nums[i - 1] == nums[i]) {
	continue;
}
//当前位置的第二次循环,如果nums[i - 1] == nums[i],则nums[i]已经在之前向下递归尝试过了,舍弃,去重
class Solution {
	public List<List<Integer>> fourSum(int[] nums, int target) {
		Arrays.sort(nums);
		dfs(nums, target, 4, 0);
		return res;
	}
	private void dfs(int[] nums, int target, int falg, int start) {
		if (falg == 0 && target == sum) {
			res.add(new ArrayList<Integer>(list));
			return;
		}
		for (int i = start; i < nums.length; i++) {
			if(nums.length - i  < 4 - list.size()) {
				return;
			}//剩余的数量,不够凑成4,直接return
            if(start != i && nums[i - 1] == nums[i]) {
            	continue;
            }//当前位置的第二次循环,如果nums[i - 1] == nums[i],则nums[i]已经在之前向下递归尝试过了,舍弃,去重
            if(i < nums.length - 1 && sum + nums[i] + (3 - list.size()) * nums[i + 1] > target) {
            	return;
            }//当下可能,最小的数值加起来大于目标值,剩余数组中不可能含有目标值,直接返回
            if(i < nums.length - 1 && sum + nums[i] + (3 - list.size()) * nums[nums.length - 1] < target) {
            	continue;
            }//当前可能,最大的数值加起来小于目标值,当前数值不用往下递归,直接遍历尝试下一个元素
			list.add(nums[i]);
			sum += nums[i];
			dfs(nums, target, falg-1, i+1);
			list.remove(list.size()-1);
			sum -= nums[i];
		}
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值