Leetcode15 第十五题
先来题诗:
一顿操作猛如虎,点击提交超时了。
二话不说翻题解,评论区里全人才。
反反复复终得道,再次尝试却报错。
行行检查字字改,击败用户百分五。
解题思路:
这道题真的让爷服了,一眼看去很简单,直接暴力解得了,结果是三个循环,时间复杂度是O(n^3),这也太大了,先扔进去看看,结果超时。
事实上拿三个循环暴力解,还需要解决一个问题,那就是如何避免结果重复的三元组。看了下官方解决办法是通过哈希表去解决,但是消耗了大量的内存空间。和大佬同学讨论了一下之后,觉得可以先将所有的三元组内部排序,接着将其转化成元组,元组作为列表的元素是可以再次排序的(震惊!这个知识点我居然还不知道),这样子每个三元组外部是排好序,内部也是排好序的,相邻的两个两两比较就可以,相同就删除后面那个,继续遍历,这样判断的最大时间复杂度是在排序上,最多O(n^2)一般O(nlogn).
为了优化(不超时),我们需要变成两个循环。
根据题目:找到三元组不能重复
1、可以想到,如果先排序(能保证重复出现的数字在一起,并且时间复杂度为O(nlogn),没啥影响)
2、可以在第二重循环的枚举中找到不小于当前第一重循环的枚举元素
3、和第三重循环同理,找到不小于第二重循环的枚举元素
那么能想到了排序,但是本质上还是三重循环,那么时间复杂度还是O(N^3),继续优化,将下面的两重循环变成一重循环:
可以发现我们是固定了第一个数然后去找其他两个数的,那么可以将后面两个数看成一个数,那么问题就变成了‘’在有序数组中从[i+1, len-1]这个范围内找到一个符合要求的数,那么就变成了双指针问题‘’,而这个数的值不再是mid,而是两个边界left和right的和。而指针的移动条件就是:如果当前的sum值太大,那么右指针就移动;如果sum太小,那么左指针就移动;如果值正好,那么就是当前值,并且左指针右移,右指针左移(因为是找到所有满足的解);循环的结束条件就是左右指针相遇 而双指针情况下,第二三重循环就从O(N^2)变成O(N)。同时为了保证因为前后数字重复最后构成的三元组一致,进行一个是否再列表中的判断。
代码
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
list=[]
nums.sort()
for i in range(len(nums)):
j=i+1
k=len(nums)-1
while j<k:
a=nums[i]
b=nums[j]
c=nums[k]
if b+c>-a:
k-=1
elif b+c<-a:
j+=1
elif b+c==-a:
num=[a,b,c]
if num not in list:
list.append(num)
k-=1
j+=1
return list