leetcode 15. 三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k
且 j != 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
-105 <= nums[i] <= 105

def threeSum(self, nums: List[int]) -> List[List[int]]:
    nums.sort()
    # i < j < k
    # 答案中不包含重复的三元组
    ans = []
    n = len(nums)
    for i in range(n-2):
        x = nums[i]
        if i > 0 and x == nums[i-1]:
            continue
        if x + nums[i+1] + nums[i+2] > 0:
            break
        if x + nums[n-1] + nums[n-2] < 0:
            continue
        j = i + 1
        k = n - 1
        while j < k:
            s = x + nums[j] + nums[k]
            if s > 0:
                k -= 1
            elif s < 0:
                j += 1
            else:
                ans.append([x, nums[j],nums[k]])
                j += 1
                while j < k and nums[j] == nums[j-1]:
                    j += 1
                k -= 1
                while k > j and nums[k] == nums[k+1]:
                    k -= 1
    return ans

详细解释

def threeSum(self, nums: List[int]) -> List[List[int]]:
    nums.sort()  # 对数组进行排序,方便使用双指针法
    ans = []  # 存储结果的列表
    n = len(nums)  # 获取数组的长度
    for i in range(n-2):  # 遍历数组,选择一个数作为第一个数
        x = nums[i]
        
        # 跳过重复的元素,避免结果中有重复的三元组
        if i > 0 and x == nums[i-1]:
            continue
        
        # 如果当前数和后面两个数的和已经大于 0,说明后面的三元组都不可能满足和为 0,提前结束
        if x + nums[i+1] + nums[i+2] > 0:
            break
        
        # 如果当前数和最后两个数的和已经小于 0,说明无法通过改变 j 和 k 来达到 0,跳过当前的 i
        if x + nums[n-1] + nums[n-2] < 0:
            continue
        
        # 使用双指针法,j 从 i+1 开始,k 从 n-1 开始
        j = i + 1
        k = n - 1
        while j < k:  # 双指针从两端向中间逼近
            s = x + nums[j] + nums[k]  # 计算三数之和
            if s > 0:  # 如果和大于 0,说明需要减小和,右指针向左移动
                k -= 1
            elif s < 0:  # 如果和小于 0,说明需要增大和,左指针向右移动
                j += 1
            else:  # 和为 0,找到了一个符合条件的三元组
                ans.append([x, nums[j], nums[k]])
                j += 1  # 移动左指针,寻找下一个可能的三元组
                # 跳过重复的元素,避免结果中有重复的三元组
                while j < k and nums[j] == nums[j-1]:
                    j += 1
                k -= 1  # 移动右指针,寻找下一个可能的三元组
                while k > j and nums[k] == nums[k+1]:
                    k -= 1
    return ans

在这里插入图片描述
详细解释

1.	nums.sort():
•	对数组进行排序,使得我们能够使用双指针技术。排序的目的是为了能够方便地判断是否已经超过目标值,且通过跳过重复元素来优化解法。
2.	for i in range(n-2)::
•	外层循环遍历数组中的每个元素 x,作为三元组中的第一个数。n-2 是因为至少需要两个数(j 和 k)来形成三元组。
3.	if i > 0 and x == nums[i-1]: continue:
•	这行代码跳过重复的元素。因为数组已排序,如果当前元素 x 与前一个元素相同,说明在之前已经找到过以 x 为第一个数的三元组,避免重复计算。
4.	if x + nums[i+1] + nums[i+2] > 0: break:
•	如果当前元素 x 和其后两个最小的元素之和已经大于 0,说明当前 x 无法与后面的数组合成和为零的三元组。因此直接 break,结束循环。
5.	if x + nums[n-1] + nums[n-2] < 0: continue:
•	如果当前元素 x 和数组最后两个最大的元素之和已经小于 0,说明 x 和后面的数组合无法达到和为零的条件,跳过当前的 i。
6.	双指针部分:
•	j = i + 1 和 k = n - 1:初始化左右指针,j 从 i+1 开始,k 从数组末尾开始。
•	while j < k::双指针的核心。指针 j 向右移动,k 向左移动,直到找到符合条件的三元组或指针相遇。
•	s = x + nums[j] + nums[k]:计算三数之和。
•	if s > 0::如果和大于 0,说明需要减小和,右指针 k 向左移动。
•	elif s < 0::如果和小于 0,说明需要增大和,左指针 j 向右移动。
•	else::如果和为 0,说明找到了一个符合条件的三元组,添加到结果列表 ans 中。
7.	跳过重复元素:
•	while j < k and nums[j] == nums[j-1]: j += 1:如果 nums[j] 与前一个数相同,则跳过,避免重复的三元组。
•	while k > j and nums[k] == nums[k+1]: k -= 1:如果 nums[k] 与下一个数相同,则跳过,避免重复的三元组。
8.	return ans:
•	返回包含所有不重复三元组的列表。

举例子

假设 nums = [-1, 0, 1, 2, -1, -4]:
1. 排序后: nums = [-4, -1, -1, 0, 1, 2]。
2. 第一轮循环(i = 0):
• 选定第一个数 x = -4。
• j = 1, k = 5,即 nums[j] = -1 和 nums[k] = 2。
• 计算三数之和:s = -4 + (-1) + 2 = -3,和小于
0,左指针 j 向右移动。
• 继续进行类似的操作,直到找到符合条件的三元组。
3. 最终结果: [[-1, -1, 2], [-1, 0, 1]]。

时间复杂度分析
• 时间复杂度: O(n^2),
其中 n 是数组的长度。外层循环遍历 n 个元素,内层双指针遍历最多 n 次,因此总体时间复杂度是 O(n^2)。
• 空间复杂度: O(1),除了存储结果列表 ans 外,使用的额外空间是常数级别。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值