[面试精选] 0015. 三数之和

文章目录

      • 1. 题目链接
      • 2. 题目描述
      • 3. 题目示例
      • 4. 解题思路
      • 5. 题解代码
      • 6. 复杂度分析

1. 题目链接


15. 三数之和 - 力扣(LeetCode)


2. 题目描述


给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

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

3. 题目示例


示例 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 。

4. 解题思路


  1. 问题理解
    • 给定一个整数数组nums,找出所有不重复的三元组[nums[i], nums[j], nums[k]],使得i != j != knums[i] + nums[j] + nums[k] = 0
    • 需要避免返回重复的三元组。
  2. 关键思路
    • 排序预处理:先对数组排序,便于后续双指针操作和去重。
    • 固定一个数+双指针:固定第一个数nums[k],然后在剩余部分使用双指针ij寻找另外两个数。
    • 去重处理:通过跳过重复元素来避免重复的三元组。
  3. 算法流程
    • 排序:首先对数组进行排序。
    • 外层循环:遍历数组,固定第一个数nums[k]
      • 提前终止:如果nums[k] > 0,直接退出循环(因为排序后后续数都大于0,不可能和为0)。
      • 跳过重复:如果nums[k]与前一个数相同,跳过以避免重复。
    • 内层双指针:使用双指针ijk+1nums.length-1范围内寻找另外两个数。
      • 和的计算:计算nums[k] + nums[i] + nums[j]
        • 如果和大于0,移动j向左。
        • 如果和小于0,移动i向右。
        • 如果和等于0,记录三元组,并跳过重复的ij

5. 题解代码


class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        // 1. 先对数组进行排序,便于后续双指针操作
        Arrays.sort(nums);
        List<List<Integer>> ans = new ArrayList<>();
        
        // 2. 遍历数组,固定第一个数nums[k]
        for (int k = 0; k < nums.length - 2; k++) {
            // 2.1 如果第一个数已经大于0,后面不可能和为0,直接退出
            if (nums[k] > 0) break;
            
            // 2.2 跳过重复的第一个数
            if (k > 0 && nums[k] == nums[k - 1]) continue;
            
            // 3. 双指针法:i从k+1开始,j从末尾开始
            int i = k + 1, j = nums.length - 1;
            while (i < j) {
                int sum = nums[k] + nums[i] + nums[j];
                
                if (sum < 0) {
                    // 3.1 和太小,需要增大i,同时跳过重复元素
                    while (i < j && nums[i] == nums[++i]);
                } else if (sum > 0) {
                    // 3.2 和太大,需要减小j,同时跳过重复元素
                    while (i < j && nums[j] == nums[--j]);
                } else {
                    // 3.3 找到和为0的三元组
                    ans.add(new ArrayList<>(Arrays.asList(nums[k], nums[i], nums[j])));
                    // 跳过重复的i和j
                    while (i < j && nums[i] == nums[++i]);
                    while (i < j && nums[j] == nums[--j]);
                }
            }
        }
        return ans;
    }
}


6. 复杂度分析


  1. 时间复杂度
    • 排序:O(n log n)。
    • 外层循环:O(n),每个元素最多被访问一次。
    • 内层双指针:O(n),每个元素最多被访问一次。
    • 总时间复杂度:O(n²)(排序O(n log n) + 双指针O(n²))。
  2. 空间复杂度
    • 排序:O(log n)(Java的排序算法使用栈空间)。
    • 存储结果:O(n)(最坏情况下可能有O(n²)个三元组,但实际远小于此)。
    • 总空间复杂度:O(n)(忽略排序的栈空间)。

你可能感兴趣的:(算法,面试精选)