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:盛水最多的容器,但是这题没有想到用这个,自己还是不够灵活分析问题...
思路:
首先我们对数组先排序一次,在排好序的数组上,就很容判断前后元素是否相当,这样可以过滤掉重复的答案。
再定义三个指针,k
,i
,j
如下图所示
指针i
从左往右移动,且始终比k
大一位,这样就保证不会跟k
重叠,
指针j
从右往左移动,且始终比i
大,这样i
和j
就不会重叠,即三个指针都不会重叠。
- 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 之和是否存在于哈希表中.
需要注意的点:
-
找到满足条件的结果后, 需要将结果数组序列化并存入令一个哈希表中, 以便对结果去重
-
首先在对 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