力扣刷题(python)50天——第九天:三数之和

力扣刷题(python)50天——第九天:三数之和

题目描述

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

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

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解答

看到本题,第一反应就是使用指针,接下来便是规定指针的变换,最初按照我的思路,结和之前几道题的经验,先sort()排序,然后在中间n-2项中选择某项作为中间项。于是排序好的数组按中间项分为了左右两个数组,后左右设置两个指针进行检测:

class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        nums.sort()
        res=[]
        l=len(nums)
        for i in range(1,l-1):
            nums1=nums[:i]
            nums2=nums[i+1:]
            for j in nums1:
                for k in nums2:
                    if j+k+nums[i]==0:
                        if [j,nums[i],k] not in res:
                            res.append([j,nums[i],k])
        return res

问题是时间复杂度较高,初步执行了一下,对于例子中某个较长的列表,执行时间竟然有至少10min。
在查了一些资料后,得到一些化简的方式:

1.当 nums[i] > 0 时直接break跳出:因为 nums[k] >= nums[j] >= nums[i] > 0,即 3个数字都大于0 ,在此固定指针 i 之后不可能再找到结果了。
(max_v,min_v分别表示整个数组的最大,最小值)
2.if -(nums[i]+nums[j])>max_v: 直接continue
3.if -(nums[j]+nums[k]) 进行优化后,依然会有很长的运行时间,在思来想去无果后,绝对采取另一种方法:

https://leetcode-cn.com/problems/3sum/solution/3sumpai-xu-shuang-zhi-zhen-yi-dong-by-jyd/

即:将固定的数字由中间值改为将最小值固定,指针放在分割后右侧数组的右边进行寻找
这样会让检测的数组逐渐变小。而如果固定中间,则每次都要遍历数组,与暴力法相比虽然优化了很多,但依然运行时间较长,检测时间先增加后减小,呈中间峰值。

再加上之前优化的想法,于是有如下代码:

class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        if nums==[]:
            return([])
        nums.sort()
        min_v=nums[0]
        max_v=nums[-1]
        res=[]
        l=len(nums)
        for i in range(l-2):
            if nums[i]==nums[i-1] and i>0:
                continue
            elif nums[i]>0:
                continue
            nums2=nums[i+1:]
            l2=len(nums2)
            j=0
            k=l2-1
            while 1:
                try:
                    if -(nums[i]+nums2[j])>max_v:
                        j+=1
                    else:
                        break
                except:
                    break
            while k>j:
                sum1=nums[i]+nums2[j]+nums2[k]
                if sum1>0:
                    k-=1
                    if nums2[k]<0:
                        continue
                elif sum1<0:
                    j+=1
                else:
                    if [nums[i],nums2[j],nums2[k]] not in res:
                        res.append([nums[i],nums2[j],nums2[k]])
                    k-=1
                    if nums2[k]<0:
                        continue
        return res

时间以及大幅减少,但依然有8秒的运行时间,到底是哪里出了问题呢!!!!!!!!

https://leetcode-cn.com/problems/3sum/solution/3sumpai-xu-shuang-zhi-zhen-yi-dong-by-jyd/

经过对比我发现,区别在于检测数组是否已经存在浪费了巨大的时间

if [nums[i],nums2[j],nums2[k]] not in res:
                        res.append([nums[i],nums2[j],nums2[k]])

因为每次执行这句都会遍历整个res,在res较长的情况下会浪费巨量的时间。
*

解决: 由于数组已经排序,因此每次都检测指针指向的数字是否与前或后一个数相等,若满足则直接跳过该指针,这样就能保证一定不会重复,因此跳过重复检测,最终代码:

class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        if nums==[]:
            return([])
        nums.sort()
        max_v=nums[-1]
        res=[]
        l=len(nums)
        for i in range(l-2):
            if nums[i]==nums[i-1] and i>0:
                continue
            elif nums[i]>0:
                break
            j=i+1
            k=l-1

            while jmax_v:
                    j+=1
                else:
                    break

            while k>j and nums[k]>=0:
                s=nums[i]+nums[j]+nums[k]
                if s>0:
                    k-=1
                elif s<0:
                    j+=1
                else:
                    res.append([nums[i],nums[j],nums[k]])
                    k-=1
                    j+=1
                    while j < k and nums[j] == nums[j - 1]:
                    	 j += 1
                    while j < k and nums[k] == nums[k + 1]: 
                    	 k -= 1
        return res

执行结果

成绩还行吧
力扣刷题(python)50天——第九天:三数之和_第1张图片

心得:

经过这题对时间的概念加深了很多,应该尽量避免 in 等遍历操作,同时注意数据特点,在特定条件下及时直接跳入下一循环,减少明显多余的操作,降低时间,收获很大。

PS:貌似也可以用集合的形式来储存列表来避免元素重复,但列表不可哈希,因此是否可以用集合储存元组来试试呢?

你可能感兴趣的:(leetcode刷题)