zoukankan      html  css  js  c++  java
  • 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
    
  • 相关阅读:
    准备工作
    个人作业感言
    年度书单-结对编程
    案例分析
    编程作业_词频统计
    2、阅读任务
    1、准备工作
    个人作业获奖感言
    3 20210405-1 案例分析作业
    202103226-1 编程作业
  • 原文地址:https://www.cnblogs.com/xiaoqiangink/p/13025818.html
Copyright © 2011-2022 走看看