目 录CONTENT

文章目录

Leetcode 18. 四数之和(画图分析)

小王同学
2024-04-14 / 0 评论 / 0 点赞 / 44 阅读 / 0 字

Leetcode 18. 四数之和(画图分析)

力扣传送门(opens new window)

题意:给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。

注意:

答案中不可以包含重复的四元组。

示例: 给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。 满足要求的四元组集合为: [ [-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2] ]

解题思路:

对数组进行排序,使用四个指针 i、j 、left 和 right 分别代表要找的四个数。

排序的主要目的是为了便于使用双指针法,并且在一定程度上减少时间复杂度。

  • 便于使用双指针法:在四数之和问题中,排序后可以让我们固定两个数(外层两个循环),然后使用双指针法查找剩余的两个数,使得时间复杂度从 O(N³) 降到 O(N²)(双指针法在排好序的数组中效率更高)。
  • 去重:排序后,可以方便地跳过相同的元素来去重。
  • 剪枝:排序后,如果固定的前两个数的和已经大于目标值 target,我们就可以提前停止搜索,避免不必要的计算。

接着我们通过枚举 i 确定第一个数,枚举 j 确定第二个数,另外两个指针 left 和 right 分别从左边 j + 1 和右边 n - 1 往中间移动,找到满足 nums[i] + nums[j] + nums[left] + nums[right] == target 的所有组合。

更加详细的可以看我下面画的图。
left 和 right 指针的移动逻辑,分情况讨论 sum = nums[i] + nums[j] + nums[left] + nums[right] :
sum >target:right 左移,使sum 变小
sum <target:left 右移,使sum 变大
sum =target:将组合加入结果集,left 右移继续进行检查
题目要求不能包含重复元素,所以我们要对 i、j 和 left 进行去重,去重逻辑是对于相同的数,只使用第一个。

下面的图根据nums = [1, 0, -1, 0, -2, 2],和 target = 0,来进行的步骤分析

sum = nums[i] + nums[j] + nums[left] + nums[right]

四数之和.jpg

代码实现:

Java

image-bruh.png

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> res = new ArrayList<>();
        if(nums == null || nums.length == 0){
            return res;
        }
        // 排序
        Arrays.sort(nums);
        for(int i = 0; i < nums.length; i++){
            // 剪枝,如果当前数字大于目标值且是正数,则直接退出循环
            if(target > 0 && nums[i] > target){
                break;
            }
            // 去重,跳过相同的元素
            if(i > 0 && nums[i] == nums[i - 1]){
                continue;
            }
            for(int j = i + 1; j < nums.length; j++){
                // 剪枝,如果当前组合的和已经大于目标值,则退出
                if(nums[i] + nums[j] > 0 && nums[i] + nums[j] > target){
                    break;
                }
                // 去重,跳过相同的元素
                if(j > i + 1 && nums[j] == nums[j - 1]){
                    continue;
                }
                int left = j + 1;
                int right = nums.length - 1;
                // 双指针寻找剩余的两个数
                while(left < right){
                    int sum = nums[i] + nums[j] + nums[left] + nums[right];
                    if(sum == target){
                        res.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
                        // 跳过重复的 left 和 right
                        while(left < right && nums[left] == nums[left + 1]){
                            left++;
                        }
                        while(left < right && nums[right] == nums[right - 1]){
                            right--;
                        }
                        left++;
                        right--;
                    } else if(sum > target){
                        right--; // 如果和大于目标,右指针左移
                    } else {
                        left++; // 如果和小于目标,左指针右移
                    }
                }
            }
        }
        return res;
    }
}

python3

image-lbfq.png

class Solution:
    def fourSum(self, nums, target):
        res = []
        if not nums:
            return res
        nums.sort()  # 排序
        for i in range(len(nums)):
            # 剪枝,如果当前数字大于目标值且是正数,直接退出循环
            if target > 0 and nums[i] > target:
                break
            # 去重,跳过相同的元素
            if i > 0 and nums[i] == nums[i - 1]:
                continue
            for j in range(i + 1, len(nums)):
                # 剪枝,如果当前组合的和已经大于目标值,退出
                if nums[i] + nums[j] > 0 and nums[i] + nums[j] > target:
                    break
                # 去重,跳过相同的元素
                if j > i + 1 and nums[j] == nums[j - 1]:
                    continue
                left, right = j + 1, len(nums) - 1
                # 双指针寻找剩余的两个数
                while left < right:
                    sum_val = nums[i] + nums[j] + nums[left] + nums[right]
                    if sum_val == target:
                        res.append([nums[i], nums[j], nums[left], nums[right]])
                        # 跳过重复的 left 和 right
                        while left < right and nums[left] == nums[left + 1]:
                            left += 1
                        while left < right and nums[right] == nums[right - 1]:
                            right -= 1
                        left += 1
                        right -= 1
                    elif sum_val > target:
                        right -= 1  # 如果和大于目标,右指针左移
                    else:
                        left += 1  # 如果和小于目标,左指针右移
        return res

c++

image-bhui.png

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> res;
        if(nums.empty()) return res;
        sort(nums.begin(), nums.end()); // 排序
        for(int i = 0; i < nums.size(); i++){
            // 剪枝,如果当前数字大于目标值且是正数,直接退出循环
            if(target > 0 && nums[i] > target) break;
            // 去重,跳过相同的元素
            if(i > 0 && nums[i] == nums[i - 1]) continue;
            for(int j = i + 1; j < nums.size(); j++){
                // 剪枝,如果当前组合的和已经大于目标值,退出
                if(nums[i] + nums[j] > 0 && nums[i] + nums[j] > target) break;
                // 去重,跳过相同的元素
                if(j > i + 1 && nums[j] == nums[j - 1]) continue;
                int left = j + 1, right = nums.size() - 1;
                // 双指针寻找剩余的两个数
                while(left < right){
                    int sum = nums[i] + nums[j] + nums[left] + nums[right];
                    if(sum == target){
                        res.push_back({nums[i], nums[j], nums[left], nums[right]});
                        // 跳过重复的 left 和 right
                        while(left < right && nums[left] == nums[left + 1]) left++;
                        while(left < right && nums[right] == nums[right - 1]) right--;
                        left++;
                        right--;
                    } else if(sum > target){
                        right--; // 如果和大于目标,右指针左移
                    } else {
                        left++; // 如果和小于目标,左指针右移
                    }
                }
            }
        }
        return res;
    }
};

  • 空间复杂度:O(N + K),其中 N 是数组长度,K 是结果集的大小。
  • 时间复杂度:O(N³),排序是 O(N log N),双指针部分是 O(N³)。
0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区