LeetCode题练习与总结:三数之和

167 篇文章 0 订阅
100 篇文章 0 订阅

一、题目描述

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

示例 2:

输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。

示例 3:

输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。

提示:

  • 3 <= nums.length <= 3000
  • -10^5 <= nums[i] <= 10^5

二、解题思路

  1. 排序数组:首先,我们需要对数组进行排序。这样可以帮助我们在后续步骤中更有效地找到满足条件的三元组。

  2. 遍历数组:然后,我们遍历数组,对于每个元素 nums[i],我们使用两个指针,一个指向当前元素的下一个元素(左指针),另一个指向数组的最后一个元素(右指针)。

  3. 双指针法:对于当前元素 nums[i],我们尝试找到两个其他元素,使得这三个元素的和为0。我们移动左指针和右指针,使得左指针指向的元素和右指针指向的元素的和加上当前元素 nums[i] 是否等于0。如果和小于0,我们向右移动左指针;如果和大于0,我们向左移动右指针。这样我们可以保证在每一步中,我们都在寻找和为0的三元组。

  4. 去重:在找到满足条件的三元组后,我们需要确保返回的结果中不包含重复的三元组。这可以通过在添加三元组到结果列表之前检查是否已经存在相同的三元组来实现。

  5. 返回结果:最后,我们返回包含所有满足条件的三元组的列表。

三、具体代码

import java.util.ArrayList;
import java.util.List;

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        if (nums == null || nums.length < 3) {
            return result;
        }

        // Step 1: Sort the array
        Arrays.sort(nums);

        // Step 2: Iterate through the array
        for (int i = 0; i < nums.length - 2; i++) {
            // To avoid duplicates, we skip the same element
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }

            // Step 3: Use two pointers
            int left = i + 1;
            int right = nums.length - 1;

            while (left < right) {
                int sum = nums[i] + nums[left] + nums[right];

                // Check if the sum is 0
                if (sum == 0) {
                    // Add the triplet to the result set and move both pointers
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));

                    // Skip duplicates
                    while (left < right && nums[left] == nums[left + 1]) {
                        left++;
                    }
                    while (left < right && nums[right] == nums[right - 1]) {
                        right--;
                    }
                    left++;
                    right--;
                } else if (sum < 0) {
                    // If sum is less than 0, move left pointer to the right
                    left++;
                } else {
                    // If sum is greater than 0, move right pointer to the left
                    right--;
                }
            }
        }

        return result;
    }
}

四、时间复杂度和空间复杂度

1. 时间复杂度
  • 排序数组O(n log n),因为数组排序通常使用快速排序、归并排序等,其平均时间复杂度为 O(n log n)
  • 遍历数组O(n),因为我们只需要遍历数组一次。
  • 双指针法:在最坏的情况下,对于数组中的每个元素,双指针可能需要遍历剩余的数组元素,这在最坏情况下是 O(n^2)。但是,由于我们在找到和为0的三元组后会移动指针,所以实际的时间复杂度会小于 O(n^2)。具体来说,每次移动左指针或右指针时,我们都会跳过重复的元素,这减少了实际的比较次数。

综合考虑,时间复杂度可以近似为 O(n^2),因为排序是预处理步骤,而双指针法是主要的时间消耗部分。

2. 空间复杂度
  • 结果列表:在最坏的情况下,如果数组中的所有元素都满足三元组和为0的条件,那么结果列表的大小将是 O(n^2)。这是因为我们可能会添加 n * (n - 1) / 2 个三元组到结果列表中(假设数组中的元素都是唯一的)。
  • 辅助空间:除了结果列表,我们还需要额外的空间来存储排序后的数组索引(即左指针和右指针),这在最坏情况下是 O(1)

所以,空间复杂度是 O(n^2)(结果列表)加上 O(1)(指针),可以简化为 O(n^2)

请注意,这里的分析是基于最坏情况的假设。在实际应用中,由于数组中可能存在负数、正数和零,且它们的分布可能不均匀,实际的时间复杂度可能会有所不同。

五、总结知识点

1. 数组排序

  • 使用 Arrays.sort(nums) 方法对整数数组进行排序。这是 Java 标准库中的一个方法,用于对数组进行快速排序。

2. 数组遍历

  • 使用 for 循环遍历数组,这是 Java 中常见的迭代数组元素的方法。

3. 条件判断

  • 在遍历过程中,使用 if 语句跳过重复的元素,以避免在结果中出现重复的三元组。

4. 双指针技巧

  • 使用两个指针(leftright)来遍历数组的剩余部分,这是一种高效的算法技巧,用于在有序数组中寻找特定条件的元素组合。

5. 列表操作

  • 使用 ArrayListArrays.asList() 方法来创建和添加列表元素。ArrayList 是 Java 中的一个动态数组,可以动态地添加和删除元素。

6. 去重处理

  • 在添加三元组到结果列表之前,通过移动指针来跳过重复的元素,这是一种常见的去重策略。

7. 逻辑控制

  • 使用 while 循环结合条件判断(if-else)来控制指针的移动,这是解决这类问题的关键逻辑。

8. 返回结果

  • 最后,将包含所有满足条件的三元组的列表返回给调用者。

9. 代码风格

  • 代码遵循了一定的命名规范和结构,如方法名 threeSum 清晰地表达了其功能,变量名如 resultleftright 等也直观地反映了它们的用途。

以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。

  • 31
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一直学习永不止步

谢谢您的鼓励,我会再接再厉的!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值