LeetCode 15. 三数之和

题目:


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

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

示例:

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

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


思路:

首先想到的是按照位去依次相加,得到为0的元素就塞入数组,直到找到所有组合情况,注意删除重复元素。

方法一的逻辑测试有实现我预期的功能,但是测试用例的预期结果有些怪异。

输入:[-1,0,1,2,-1,-4]
输出:[[-1,0,1],[-1,2,-1],[0,1,-1]]
预期结果:[[-1,-1,2],[-1,0,1]]

这么看起来,[-1,0,1]和[0,1,-1]也算相同,需要去重,在想了很久之后,终于写出一个按照我理解的思路的方法,不过超时了...Q_Q, 但是思路很美丽啊,哈哈哈。

方法一:

import copy
class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        nums.sort() #从大到小排序,方便后面的去重
        length = len(nums)
        length_count = length*(length-1)*(length-2)/3/2 #定义array count,为Cn^3阶乘,用于存放三元和为0的数组
        count = 0 #空数组count
        temp_list = [[] for _ in range(length_count)] #声明定义阶乘数量的二维数组
        for i in range(length):
            j = i
            while j+1<length-1 :
                j +=1
                k = j+1
                while k <= length-1:
                    if  nums[i]+nums[j]+nums[k] == 0:
                        # print ' Case 0 == ',i,j,k,nums[i],nums[j],nums[k]
                        for m in range (len(temp_list)):
                            if len(temp_list[m]) ==0 : #将三元数相加和为0的塞入二维数组->元素为空的二维数组index才塞入新元素。
                                temp_list[m].append(nums[i])
                                temp_list[m].append(nums[j])
                                temp_list[m].append(nums[k])
                                break
                    k +=1
        for v in range(len(temp_list)): #找到空数组数量
            if len(temp_list[v]) ==0:
                count +=1
        for i in range(count): #删除空数组(因为空数组只会在最后,所以用pop即可)
            temp_list.pop()
        list_arr = copy.copy(temp_list) #浅copy一次arr,用于下面的枚举遍历,因为需要remove原arr中的相同元素,remove后不能改变其枚举值,所以需要copy一份。
        # print 'temp_list before = ',temp_list
        for k,v in enumerate(list_arr): #删除重复元素
            while temp_list.count(v) >=2:
                temp_list.remove(v)
        print 'temp_list After = ',temp_list
        return temp_list

方法二:

方法一超时的原因是是在处理相同元素的时候里面有四层循环......看着傻傻滴,那就换个思路去处理好了。既然我有用到排序和j k来定义第二位和第三位,我是不是应该用j ,k直接来获取结果会更方便些。

参考题解王尼玛的处理分享,思想就是双指针,参考leecode 11:盛水最多的容器,但是这题没有想到用这个,自己还是不够灵活分析问题...

思路:

首先我们对数组先排序一次,在排好序的数组上,就很容判断前后元素是否相当,这样可以过滤掉重复的答案。
再定义三个指针,kij如下图所示

指针i从左往右移动,且始终比k大一位,这样就保证不会跟k重叠,
指针j从右往左移动,且始终比i大,这样ij就不会重叠,即三个指针都不会重叠。

  • nums[k]>0时,可以直接跳出循环,因为nums[k]都比0大了,后面的nums[i]和nums[j]肯定更大,三者加起来肯定大于0
  • nums[k]和nums[k-1]相等,即前后元素重复了,需要过滤掉
  • 如果nums[i]+nums[j]+nums[k]>0,即三者之和太大了,我们将j指针左移,因为三个数中最大的肯定是nums[j],将j左移就可以减小三者之和。
  • 如果nums[i]+nums[j]+nums[k]<0,说明三者之和太小了,同理将i指针右移
  • 如果nums[i]+nums[j]+nums[k]==0,这就是要找的答案,将其保存起来,同时i右移,j左移
  • i和j在移动的过程中还需要判断前后元素是否重复

class Solution(object):
	def threeSum(self, nums):
		if not nums:
			return []
		# 正式处理之前,先将数组排序	
		nums = sorted(nums)
		n = len(nums)
		res = []
		# 假设数组为[0,1,2,3,4,5,6,7,8,9,10]
		# 第三个指针k最多到下标8位置,因为后面两个位置需要留给另外两个指针
		for k in xrange(n-2):
			# nums[k]>0,说明后面的元素肯定也大于0,最后结果肯定>0,故直接跳出
			if nums[k]>0:
				break
			# 如果当前元素和前面一个元素一样,忽略重复元素	
			if k>0 and nums[k-1]==nums[k]:
				continue
			# 定义另外两个指针 i 和 j 	
			i,j = k+1,n-1
			while i<j:
				tmp = nums[i]+nums[j]+nums[k]
				# 如果三数之和>0,说明最右边的值太大了,
				if tmp>0:
					j -= 1
					while i<j and nums[j+1]==nums[j]:
						j -= 1
				# 如果三数之和<0,说明左边的值太小了		
				elif tmp<0:
					i += 1
					while i<j and nums[i-1]==nums[i]:
						i += 1
				# 三数之和等于0,保存结果
				# 同时左指针往右移动,右指针往左移动,
				# 如果移动过程中碰到重复元素,则继续移动
				else:
					res.append([ nums[k],nums[i],nums[j] ])
					i += 1
					j -= 1
					while i<j and nums[i-1]==nums[i]:
						i += 1
					while i<j and nums[j+1]==nums[j]:
						j -= 1
		return res

方法三:

这个出自题解 Xiaojing CHEN的解题思路,方法思想很简单,由
a + b + c = 0
等价于
a + b = -c

先对数组进行排序, 再对排序后的数组进行遍历, 将每个元素的相反数作为key, 元素所在的位置作为value存入哈希表中, 两次遍历数组不断检查 a + b 之和是否存在于哈希表中.

需要注意的点

  1. 找到满足条件的结果后, 需要将结果数组序列化并存入令一个哈希表中, 以便对结果去重

  2. 首先在对 a,b 进行遍历时, 如果当前元素与前一个元素相同可直接跳过以优化性能 (思考: 后一个元素能发现的结果一定会包含在前一个元素的结果中). 另外, 仅在一层循环中加入此逻辑性能最佳. 该逻辑有效的前提是相同的元素需要连在一起, 所以需先对数组进行排序

class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        if len(nums) < 3:
            return []
        '''先对数组排序, 遍历数组遇到与前一个元素相同的情况可直接跳过'''
        nums.sort()
        target_hash = {-x: i for i, x in enumerate(nums)}
        res = []
        res_hash = {}
        for i, first in enumerate(nums):
            '''当前元素与前一个元素相同时, 可直接跳过以优化性能'''
            if i > 0 and first == nums[i - 1]:
                continue
            for j, second in enumerate(nums[i + 1:]):
                '''检查两数之和是否存在于哈希表中'''
                if first + second in target_hash:
                    target_index = target_hash[first + second]
                    if target_index == i or target_index == i + j + 1:
                        continue
                    '''将找到的结果存入另一个哈希表中, 避免包含重复结果'''
                    row = sorted([first, second, nums[target_index]])
                    key = ",".join([str(x) for x in row])
                    if key not in res_hash:
                        res.append(row)
                        res_hash[key] = True
        return res
posted @ 2020-06-01 16:18  萧蔷ink  阅读(138)  评论(0编辑  收藏  举报