topic15:三数之和
题目描述:
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
思路1:暴力解法,先用三重循环,求出满足条件的所有可能,然后去重——先将三元组排序,再用set去重,最后再强制转化为list,时间复杂度为O(n3),空间复杂度为O(n)
def threeSum(nums1): #犹豫不决先排序,步步紧逼双指针
nums=nums1
result=[]
for i in range(0,len(nums)):
for j in range(i+1,len(nums)):
for k in range(j+1,len(nums)):
if (i!=j and i!=k and j!=k and nums[i] + nums[j] + nums[k] == 0 ): #最坏情况,还得去重
result.append([nums[i],nums[j],nums[k]])
if len(result)==0:
return []
for i in range(0,len(result)):
result[i]=sorted(result[i])
new_list = list(set(map(tuple, result)))
return new_list
思路2:排序加双指针——先将列表的元素排序,然后遍历选定数组元素,令左指针L=i+1,右指针R=n-1,当L<R时,执行循环,求和当sum<0时,说明nums[L]太小,左指针向右移,当sum>0时,说明nums[R]太大,右指针向左移,当sum=0时,记录,开始下一个外层循环。时间复杂度O(n2),空间复杂度O(1)
我用双指针思路写的版本,但是达不到去重效果(费解)——先输出结果再去重
def threeSum(nums): #犹豫不决先排序,步步紧逼双指针
sortnums=sorted(nums)
result=[] #初始化一个列表,用来储存最后的结果
for i in range(0,len(sortnums)):
left=i+1 #左指针
right=len(sortnums)-1 #右指针
if sortnums[i]>0: #如果选定的元素大于0,则其右侧的元素都>0,不满足条件
return result
while left<right:
if sortnums[i]+sortnums[left]+sortnums[right]==0:
result.append([sortnums[i],sortnums[left],sortnums[right]]) #满足条件,左指针右移,右指针左移
right-=1
left+=1
elif sortnums[i]+sortnums[left]+sortnums[right]>0: #值偏大,右指针左移
right-=1
elif sortnums[i]+sortnums[left]+sortnums[right]<0: #值偏小,左指针右移
left+=1
new_list=list(set(map(tuple,result))) #利用集合中元素的唯一性对列表重复出现的去重
return new_list
测试通过代码:先去重再输出结果
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
n=len(nums)
res=[]
if(not nums or n<3):
return []
nums.sort()
res=[]
for i in range(n):
if(nums[i]>0):
return res
if(i>0 and nums[i]==nums[i-1]):
continue
L=i+1
R=n-1
while(L<R):
if(nums[i]+nums[L]+nums[R]==0):
res.append([nums[i],nums[L],nums[R]])
while(L<R and nums[L]==nums[L+1]):
L=L+1
while(L<R and nums[R]==nums[R-1]):
R=R-1
L=L+1
R=R-1
elif(nums[i]+nums[L]+nums[R]>0):
R=R-1
else:
L=L+1
return res
topic16,最接近的三数之和(与上题一样,多了个不等的可能,不需要去重,想等则退出遍历,求其差得绝对值最小值)
时间复杂度O(n2),空间复杂度O(1)
def threeSumClosest(nums, target):
result = 200000
nums.sort()
length = len(nums)
for i in range(length - 2):
left = i + 1
right = length - 1
while left < right:
tmp = nums[i] + nums[left] + nums[right]
result = tmp if abs(tmp - target) < abs(result - target) else result
if tmp == target:
return target
if tmp > target:
right -= 1
else:
left += 1
return result
topic17:电话号码的组合
题目描述:
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
思路:用两个列表构造数字和字母间的映射关系,最后循环输出,思路简单,但代码比想像中难写
当题目出现所有组合等类似字眼的时候,就得想起回溯(比如马踏棋盘)
回溯算法用于寻找所有的可行解,如果发现一个解不可行,则会舍弃不可行的解。在这道题中,由于每个数字对应的每个字母都可能进入字母组合,因此不存在不可行的解,直接穷举所有的解即可。
def letterCombinations(self, digits: str) -> List[str]:
# 如果digits为空,则返回一个空列表
if not digits: return []
# 定义一个字典,将每个数字映射成一个字符列表,方便后续的回溯操作
phone = {'2':['a','b','c'],
'3':['d','e','f'],
'4':['g','h','i'],
'5':['j','k','l'],
'6':['m','n','o'],
'7':['p','q','r','s'],
'8':['t','u','v'],
'9':['w','x','y','z']}
# 定义一个回溯函数,接收当前的组合和剩余的数字
def backtrack(conbination,nextdigit):
# 如果剩余数字为空,则将当前组合加入到结果列表中
if len(nextdigit) == 0:
res.append(conbination)
else:
# 否则,枚举下一个数字所对应的字符列表中的每一个字符,并递归调用回溯函数
for letter in phone[nextdigit[0]]:
backtrack(conbination + letter,nextdigit[1:])
# 初始化结果列表
res = []
# 调用回溯函数,初始时当前组合为空字符串,剩余数字为digits
backtrack('',digits)
# 返回结果列表
return res