【LeetCode】三数之和——15_threeSum

版权声明:本文版权归作者和CSDN共有,欢迎转载。转载时请注明原作者并保留此段声明,若不保留我也不咬你,随你了=-=。 https://blog.csdn.net/TeFuirnever/article/details/89231965

今天开始正式进行LeetCode的刷题之旅,自己的编程算法能力都太差了,所以决定进行苦练,查到的代码和解法,优化以及个人的理解,就这样。

在这里插入图片描述

LeetCoddehttps://blog.csdn.net/TeFuirnever/column/info/36614

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

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

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

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

题解1:

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

class Solution:
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        from collections import Counter
        k = []
        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

考虑结果重复的问题,我们通过collections.Counter记录所有数字出现的次数,如果前后有相同的话,我们就不添加到result中去,但是这样的话会超时,该方法不可行。
在这里插入图片描述

题解2:

class Solution:
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        res = []
        nums.sort()
        for i in range(len(nums) - 2):
        	# 排好序之后,如果nums[i]>0,说明后面的数全部大于0
        	if nums[i] > 0:
        		break
        	if i==0 or nums[i] > nums[i-1]:# 去重	
	        	left, right = i + 1, len(nums) - 1
	        	# 剪枝
	        	if nums[i] + nums[left] + nums[left+1] > 0 or nums[i] + nums[right - 1] + nums[right] < 0:
	        		continue
	        	while left < right:
	        		ident = nums[i] + nums[left] + nums[right]
	        		if ident==0:
	        			res.append([nums[i],nums[left],nums[right]])
	        			left += 1
	        			right -= 1
	        			# 去重
	        			while left < right and nums[left]==nums[left - 1]:
	        				left += 1
	        			while left < right and nums[right]==nums[right + 1]:
	        				right -= 1
	        		elif ident < 0:
	        			left += 1
	        		else:
	        			right -= 1
        return res

这个算法是和我自己写的算法比较相似的,但是我没有好的办法解决去重复,所以就借用了这个算法。

主要是先确定i,for循环【len(nums)-2】次,然后对left和right进行两个方向的遍历,从最外侧向最内侧遍历,因为是排好顺序的列表,所以相同的元素在一块,正负也都有位置(左侧负数,右侧正数)。

要求的是a+b+c=0 其实就是要求a+b=-c,那么问题可以转化为依次遍历数组元素c,然后在剩下的数中做两数之和为-c的问题。问题在于如何简化算法以及优化复杂度。
1.首先可以先排序(O(nlogn)),这样保证数组有序之后可以利用大小关系判断。
2.设置两个指针left、right,分别从左边以及右边向中间遍历,如果找到a+b+c==0,那么可以将这个答案加入到答案集里,如果a+b+c<0,此时固定的是c,说明a+b太小了,因此left+=1;如果a+b+c>0,此时a+b过大,因此right-=1。
3.去重,这一步则是利用了有序性,如果两个数相同,那他们在数组的位置一定是相邻的(连着几个数相同也是可能的),因此去重的操作就能简单遍历一下相邻的是否相同即可。由于数组有序性使得去重这一步很简单,因此也可以看出第一步的作用。
此外还有一些小细节的地方,比如说当遍历到c>0的时候,由于之后的数都是正数,那三数之和一定大于0,就没必要继续遍历c了(因为继续向后遍历c只会更大,那之后的数加起来一定大于0); 或者固定c,如果c及其后面连着两个数a,b,他们的和已经大于0了,就没必要进行下一步的操作,此时遍历下一个c; 同理,如果c和数组最后两个数的和仍然小于0,也没必要进行下一步操作。

看到一个专栏的解法和优化,好好学习:Leetcode 15:三数之和(最详细解决方案!!!)

题解3:

nums_hash = {}
        result = list()
        for num in nums:
            nums_hash[num] = nums_hash.get(num, 0) + 1
        if 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 - j
                if 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

猜你喜欢

转载自blog.csdn.net/TeFuirnever/article/details/89231965