给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 *a,b,c ,*使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

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

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[[-1, 0, 1],[-1, -1, 2]
]

解题思路

我们首先想到的解法是通过三重循环,于是我就写出了如下代码:

class Solution:def threeSum(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""result = []for i, a in enumerate(nums):for j, b in enumerate(nums[i + 1:]):for _, c in enumerate(nums[j + i + 2:]):if a + b + c == 0:result.append([a, b, c])return result

但是上面这个代码是有问题的,因为我们没有考虑结果重复的问题。接着我们想到可以通过collections.Counter记录所有数字出现的次数,如果前后有相同的话,我们就不添加到result中去。于是就有了下面的写法

class Solution:def threeSum(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""from collections import Counterk = []result = []for i, a in enumerate(nums):for j, b in enumerate(nums[i + 1:]):for _, c in enumerate(nums[j + i + 2:]):if a + b + c == 0 and Counter([a, b, c]) not in k:k.append(Counter([a, b, c]))for i in k:result.append(list(i.elements()))return result

但是这种写法的缺点很明显,算法的时间复杂度是O(n^3)这个级别的。我们能不能优化到O(n^2)这个级别呢?我们可以参考Leetcode 1:两数之和(最详细解决方案!!!)文中的方法,通过一个hash表来记录nums中所有元素出现的次数。

class Solution:def threeSum(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""nums_hash = {}result = list()for num in nums:nums_hash[num] = nums_hash.get(num, 0) + 1if 0 in nums_hash and nums_hash[0] >= 3:result.append([0, 0, 0])nums = sorted(list(nums_hash.keys())) # trickfor i, num in enumerate(nums):for j in nums[i+1:]:if num*2 + j == 0 and nums_hash[num] >= 2:result.append([num, num, j])if j*2 + num == 0 and nums_hash[j] >= 2:result.append([j, j, num])dif = 0 - num - jif dif > j and dif in nums_hash: # trickresult.append([num, j, dif])return result

注意上面写法中的trick,我们通过对nums_key的排序,这样我们就可以确定存入结果中的list中元素的顺序,这样就可以避免重复存入相同的元素。(大多数避免重复的问题,思路都是排序)

当然这个算法还有优化的空间,我们知道三个数和为0,那么在三个数不全为0的情况下,必然有一个正数和一个负数,那么我们可以通过两个list去存取nums中含有不重复元素的正数和负数。那样我们就不用O(n^2)n=len(nums))的时间,而只需要O(n*m)n+m=len(nums))的时间复杂度。

另外我们还知道一个条件,对于a,b,c三个数,如果a是正数,b是负数,此时存在两种情况:1)有两个数相同。2)所有数都不同。

例如:

a = 1
b = -2
c = 1
-------------
a = 3
b = -2
c = -1

对于第一种情况,当我们枚举ab的时候,只需要判断c是不是ab中的一个即可。对于第二种情况,此时c可能是正数也可能是负数,为了避免重复选取,我们选择满足c < b(如果c是负数)或者c > a(如果c是正数)的那组数。例如上面的第二个例子就不会选取,而如果a=3,b=-1,c=-2就会选取。

class Solution:def threeSum(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""nums_hash = {}result = list()for num in nums:nums_hash[num] = nums_hash.get(num, 0) + 1if 0 in nums_hash and nums_hash[0] >= 3:result.append([0, 0, 0])neg = list(filter(lambda x: x < 0, nums_hash))pos = list(filter(lambda x: x>= 0, nums_hash))for i in neg:for j in pos:dif = 0 - i - jif dif in nums_hash:if dif in (i, j) and nums_hash[dif] >= 2:result.append([i, j, dif])if dif < i or dif > j:result.append([i, j, dif])return result

此处应有掌声,非常好的解法是不是O(∩_∩)O

另外这个问题我们也可以使用Leetcode 167:两数之和 II - 输入有序数组(最详细解决方案!!!)这篇文章中提到的对撞指针的思路。我们首先要将nums排序。

-4 -1 -1  0  1  2
i   l           r
l = i+1

我们先要对nums排序,然后我们只要考虑nums[i] <= 0的部分,因为当nums[i] > 0时,必然会造成nums[i], nums[l], nums[r]全部>0,这显然不对。当i > 0时,我们要考虑nums[i - 1] == nums[i],如果成立,我们要跳出本次循环,执行++i,直到不成立为止。

所以我们就有了如下的做法

class Solution:def threeSum(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""result = list()nums_len = len(nums)if nums_len < 3:return resultl, r, dif = 0, 0, 0nums.sort()for i in range(nums_len - 2):if nums[i] > 0: breakif i > 0 and nums[i - 1] == nums[i]:continuel = i + 1r = nums_len - 1dif = -nums[i]while l < r:if nums[l] + nums[r] == dif:result.append([nums[l], nums[r], nums[i]])while l < r and nums[l] == nums[l + 1]:l += 1while l < r and nums[r] == nums[r - 1]:r -= 1l += 1r -= 1elif nums[l] + nums[r] < dif:l += 1else:r -= 1return result

我将该问题的其他语言版本添加到了我的GitHub Leetcode

如有问题,希望大家指出!!!

Leetcode 15:三数之和(最详细解决方案!!!)相关推荐

  1. [双指针|模拟] leetcode 15 三数之和

    [双指针|模拟] leetcode 15 三数之和 1.题目 题目链接 给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ? ...

  2. LeetCode 15. 三数之和(3Sum)

    15. 三数之和 15. 3Sum 题目描述 Given an array nums of n integers, are there elements a, b, c in nums such th ...

  3. Java实现 LeetCode 15 三数之和

    15. 三数之和 给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组. 注意:答案中不可以 ...

  4. LeetCode 15. 三数之和【双指针】

    15. 三数之和 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j.i != k 且 j != k ,同时还满足 nums[ ...

  5. LeetCode 15. 三数之和

    题目描述 15. 三数之和 思路 思路1 比较容易想到的就是,求三数之和等于0,可以等价于求两个数的和,然后看这个和的相反数是否在nums里面. 但是 T_T这样的话复杂度太高了,会超时,捂脸,最后三 ...

  6. LeetCode 15三数之和16最接近的三数之和

    三数之和(双指针) 题意: 给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组. 注意 ...

  7. Leetcode 15.三数之和

    Time: 20190920 Type: Medium 题目描述 给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所 ...

  8. 2021-5-11 Leetcode 15.三数之和

    1.1我的解法以及考虑到的几点问题 1)数组长度小于3的不用考虑,直接返回 2)暴力法:每一次查找不是自己选择的数字,看一下能否凑成零(但是这样不知道如何去重) 3)拆成一个数字+LC 1.两数之和的 ...

  9. Leetcode 15.三数之和 双指针 or 暴力哈希

    题目链接:传送门 题目:给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组. 暴力+ ...

最新文章

  1. webpack4.0 babel配置遇到的问题
  2. jquery的closest方法和parents方法的区别
  3. python中datetime库_一天掌握一个Python库--datetime库
  4. linux下共享库的制作及常见的问题
  5. 电脑能玩和平精英吗_电脑玩和平精英灵敏度这样设置更流畅,还能匹配手机
  6. mac版有道云笔记切换笔记后内容仍展示前一个笔记内容问题解决
  7. Android 四大组件学习之ContentProvider五
  8. 网页编码_Python获取网页编码(apparent_encoding)
  9. 气象报告是什么计算机领域,计算机辅助翻译系统在亚运气象服务方面的应用报告...
  10. 用Python进行数学建模(一)
  11. python数学公式编辑工具_也许是目前最好的数学公式编辑神器——Mathpix Snipping Tool...
  12. linux下oracle 9204 soft only,在CentOS4.3(x86_64)上安装Oracle9204 for linux(x86_64)
  13. ASO优化秘籍:0成本让一个马甲包月新增15w+自然用户
  14. Win11 连接不上NAS
  15. 第四批四川省工业产品绿色设计示范企业申报范围条件
  16. 创建前缀索引时,如何确认“最佳长度”
  17. JWT快速上手 | 黑马
  18. PyPi、pypi有啥作用?PyPi和pip有何渊源
  19. 让人喷饭的郭德纲语录
  20. t 检验的 3 种常用方法及在 Python 中使用样例

热门文章

  1. 【Android 逆向】启动 DEX 字节码中的 Activity 组件 ( DEX 文件准备 | 拷贝资源目录下的文件到内置存储区 | 配置清单文件 | 启动 DEX 文件中的组件 | 执行结果 )
  2. PYTHON健康评分
  3. 民工哥的十年程序员生涯:杭漂十年,今撤霸都!
  4. PngSplit合图分割软件
  5. [RK3568 Android12] LVDS G121EAN01.3屏幕及触摸ILI2511 调试
  6. Jmeter参数化请求
  7. rdpclip 远程桌面协议常遇到的问题
  8. 华为防火墙安全区域及安全策略配置举例
  9. linux++挖矿模式,矿池挖矿PPLNS+分配模式解析,长期挖矿收益会更高
  10. 聊聊从学校出来到现在的一些见解吧!