【力扣-15. 三数之和】Python笔记

6 阅读4分钟

一、前置知识点:排序 + 双指针

1. 数组排序

  • 作用:将无序数组变为有序,是使用对向双指针的前提。
  • 优势:排序后可以利用有序性,通过指针移动快速缩小查找范围,避免暴力枚举的 O (n³) 复杂度。
  • Python 实现nums.sort() 原地排序,时间复杂度 O (n log n)。

2. 对向双指针(左右指针)

  • 核心思想

    1. 固定一个基准元素,在其右侧子数组中用 leftright 指针向中间逼近。

    2. 根据三数之和与目标值的大小关系,移动指针:

    • 和 > 目标值 → right 左移(减小和)
    • 和 < 目标值 → left 右移(增大和)
    • 和 = 目标值 → 记录结果,并跳过重复值
  • 时间复杂度:O (n²)(排序 O (n log n) + 双指针遍历 O (n²))

  • 空间复杂度:O (1)(原地排序,仅用常数额外空间)

3. 去重技巧

  • 基准元素去重:若当前元素与前一个相同,直接跳过,避免生成重复三元组。
  • 结果元素去重:找到符合条件的三元组后,继续移动 leftright 指针,跳过所有与当前结果相同的元素。

二、经典算法题:三数之和(LeetCode 15)

题目描述

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

示例

  • 输入nums = [-1, 0, 1, 2, -1, -4]
  • 输出[[-1, -1, 2], [-1, 0, 1]]

最优解法:排序 + 双指针

核心思路

  1. 固定一个数:遍历数组,将每个元素 nums[i] 作为三元组的第一个数。
  2. 双指针找另外两个数:在 i 之后的子数组中,用 left = i+1right = len(nums)-1 指针寻找和为 -nums[i] 的两个数。
  3. 跳过重复值:在遍历和查找过程中,主动跳过重复元素,保证结果不重复。
from typing import List 
class Solution: 
    def threeSum(self, nums: List[int]) -> List[List[int]]: 
        res = [] nums.sort() # 先排序,方便双指针查找 
        n = len(nums) 
        for i in range(n - 2): # 跳过基准元素的重复值 
            if i > 0 and nums[i] == nums[i-1]: 
                continue 
            # 如果第一个数就>0,后面不可能和为0,直接break 
            if nums[i] > 0: 
                break 
            left = i + 1 
            right = n - 1 
            while left < right: 
                total = nums[i] + nums[left] + nums[right] 
                if total < 0: 
                    # 和太小,左指针右移
                    left += 1 
                elif total > 0: 
                    # 和太大,右指针左移
                    right -= 1 
                else: 
                    # 找到一个三元组 
                    res.append([nums[i], nums[left], nums[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 
         return res

代码执行示例

nums = [-1, 0, 1, 2, -1, -4] 为例:

  1. 排序后:[-4, -1, -1, 0, 1, 2]

  2. i=0nums[i]=-4):

    • left=1, right=5 → 和为 -4 + (-1) + 2 = -3 < 0left++
    • 最终无法找到和为 4 的组合,无结果
  3. i=1nums[i]=-1):

    • left=2, right=5 → 和为 -1 + (-1) + 2 = 0 → 记录 [-1, -1, 2]
    • 跳过重复后 left=3, right=4 → 和为 -1 + 0 + 1 = 0 → 记录 [-1, 0, 1]
  4. i=2:与 i=1 重复,直接跳过

  5. i=3nums[i]=0):0 > 0 不成立,但后续元素均 >0,无法和为 0,循环结束

  6. 最终结果:[[-1, -1, 2], [-1, 0, 1]]


三、关键知识点总结

1. 时间复杂度分析

  • 排序:O(n log n)
  • 双指针遍历:外层循环 O (n),内层双指针遍历 O (n),总计 O (n²)
  • 整体:O(n log n) + O(n²) = O(n²) ,远优于暴力法 O (n³)

2. 去重逻辑详解

  • 基准去重if i > 0 and nums[i] == nums[i-1] → 避免以相同数字作为第一个数,生成重复三元组。
  • 结果去重:找到和为 0 的三元组后,继续移动 leftright 指针,跳过所有与当前 left/right 相同的元素,避免同一组数字生成多次结果。

3. 剪枝优化

  • 提前终止:当 nums[i] > 0 时,由于数组已排序,后续元素均 ≥ nums[i],三数之和必然 > 0,直接 break 终止循环。

四、双指针法在 n 数之和中的通用思路

题目核心思路复杂度
两数之和哈希表 / 排序 + 双指针O(n) / O(n log n)
三数之和固定一个数 + 双指针O(n²)
四数之和固定两个数 + 双指针O(n³)
n 数之和固定 n-2 个数 + 双指针O(n^(n-1))