一、TwoSum
题目描述:
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
解题思路:
(1)显然,找到两个数的sum等于target,对一个列表直接进行循环嵌套,即可暴力解出:
设定两个索引i,j 直接对列表的每个数进行2层嵌套遍历,计算复杂度为O(N^2),空间复杂度低。代码及运行情况如下:
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
#最暴力的解法
for i in range(len(nums)):
for j in range(len(nums)):
if nums[i]+nums[j]==target and i!=j:
return i,j
(2)显然,由2个数的Sum与target进行比较,可将该问题转化为列表中的元素在约束条件target下的映射关系,即通过哈希思想来解决:
主要思路为:构建一个dict来存储列表元素在target约束条件下的映射关系。当 当前元素item不在map中时,保存以target-item为key,以当前索引i为value的映射关系。
显然,当下一次列表遍历至之前保存过的某个target-item时,即意味着满足约束条件target的映射关系出现了!
基于hash的思路,计算复杂度为O(n),但是空间复杂度较高,因为需要保存映射关系。
测试结果及代码如图:
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
map = {}
for index,item in enumerate(nums):
mid = target-item
if mid in map:
return map[mid],index
else:
map[item]=index
注意:此处在判断映射关系是否存在时,这里使用的是mid in map,而不是通过mid not in map是出于考虑mid not in map是需要对整个map进行遍历查找,而mid in map是找到即可! 前后的两种写法在最不理想的时候复杂度都是O(n),但是前者的平均复杂度肯定小于后者,下面可以试下使用mid not in map的写法,经过Leetcode测试:
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
map = {}
for index,item in enumerate(nums):
mid = target-item
if mid not in map:
map[item]=index
else:
return map[mid],index
可以看到性能上是有差距的!
小结一下:
其实在本身看到这道题目的时候,是想直接对列表进行操作,因为存在一个item1+item2=target的约束条件,直接遍历一次列表,然后每次遍历都判断traget-item1是否在列表即可,然后通过list.index()函数来取出两个元素的索引,得到题解,这个过程类似于Hash思想,只不过中间是想用Index直接完成map的过程。
实际上index函数是找到第一个满足条件的索引位置,这样对于一些特殊情况就不适用了!
对于列表元素与索引的对应关系,还是结合enumerate函数以及dict构建map比较保险!
对于这种由2个元素构成的元素条件,通过Hash思想能得到比较理想的效果。
二、ThreeSum
题目描述:
Given an array nums
of n integers, are there elements a, b, c in nums
such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
即:给定一个由n个整数组成的数组,寻找数组中的所有3个整数abc组合,满足a+b+c=0,组合中的元素不得重复。
如
Given array nums = [-1, 0, 1, 2, -1, -4],题解是:[[-1,0,1],[-1,-1,2]]
分析题目:
(1)约束条件是 a+b+c=0
(2)返回解答不得重复
解题思路:
思路一:直接对list中的每个元素a进行条件判断,即在a的条件下,寻找剩余list元素是否存在bc 满足a+b+c=0;
需要注意:为了避免多个相同的解出现在我们的题解中,通过对list排序,即list.sort之后可以方便很多处理:
sort之后,方便:
(1)如果在列表中有多个相同的元素a,则sort后必相邻,则可通过nums[i]与nums[i-1]的方式判断,避免重复解!
(2)在已知a的基础上,算法上通过以a的索引i为基础,以l,r前后去访问剩余列表中的元素。假设索引l,r分别对应b c。则我们可以方便的通过nums[l]==nums[l+1]与nums[r]==nums[r-1]来实现相同元素的过滤,避免重复解!
(3)sort之后,sum=a+b+c对应sum=nums[i]+nums[l]+nums[r],可以根据sum的正负情况,调整l 与r的索引即可,非常方便!
算法思路评估:时间复杂度上不算理想,但是也不离谱,O(n^2),但是空间复杂度很低,基本上就是创建了一个列表保存结果,LeetCode测试如下图所示
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
result=list()#用于存储符合a+b+c=0的元素组合
nums.sort()#sort之后方便对于一些重复元素的排除,以及方便我们对sum之后的结果进行调整
length = len(nums)
for i in range(length-2):#遍历sort之后列表中的每个元素,并对每个当前元素之后的列表元素通过l,r索引
#去访问,分析剩余可以跟当前元素组成满足nums[i]+nums[l]+nums[r]=0的组合i l r
if i>0 and nums[i]==nums[i-1]:#对于sort后的list出现重复直接过滤,避免有重复解
continue
l,r=i+1,length-1#当前元素之后,通过索引l,r对剩余元素列表前后访问
while l<r:#前后访问循环的终止条件
s = nums[i]+nums[l]+nums[r]
if s<0:#开始判断sum的情况,如果<0则说明sum的整体需要增加,所以将索引l右移
l+=1
elif s>0:#>0说明整体需要减少,所以索引r左移
r-=1
else:#符合题目条件,即sum=0
result.append((nums[i],nums[l],nums[r]))
while l<r and nums[l]==nums[l+1]:#过滤掉重复解!否则数组如有多个重复元素,则会重复解
l+=1
while l>r and nums[r]==nums[r-1]:
r-=1
l+=1
r-=1
return result
思路二
参考TwoSum中的Hash思路。在本题中,需要满足条件是a+b+c=0.
遍历list中的每个元素,假定当前元素为a.在此基础上,我们对剩余list的元素进行遍历寻找a+b+c=0,即b+c=-a,此时其实就把问题转化成了前面的TwoSum问题了。因为这里a是已知的,假如a=-2,那么问题就回到了TwoSum中:找到b c 满足b+c=2。只不过在ThreeSum中这个target值是多个的而已!对应ThreeSum中的多组解!
算法思路评估:
时间复杂度上,对当前元素a遍历n,对剩余list的元素以l,r遍历,实际上仅改变l的索引实现遍历,复杂度O(n^2)
空间复杂度上,因为多了一个dict实现Hash,故空间复杂度相对前者会高一些。
有了思路一的注释,思路二就不多注释了,相似:
nums.sort()
res = list()
length = len(nums)
for i,a in enumerate(nums[:-2]):
if i >= 1 and a == nums[i-1]:
continue
d = {}
l,r =i+1, length-1
while l<=r:
if nums[l] not in d:
d[-a-nums[l]]=1
l+=1
else:
res.append((a,-nums[i]-nums[l],nums[l]))
while l<r and nums[l]==nums[l+1]:
l+=1
l+=1
return res
思路三
其实与思路二一模一样,都是以元素a遍历list,在确定a之后,在剩余的list中寻找bc 转化成为一个b c的TwoSum问题。
但是思路中最主要的不同的是利用了set数据结构存储题解,利用set存储数据的唯一性来避免多个重复解。
if len(nums) < 3:
return []
nums.sort()
res = set()
for i, v in enumerate(nums[:-2]):
if i >= 1 and v == nums[i-1]:
continue
d = {}
for x in nums[i+1:]:
if x in d:
res.add((v, -v-x, x))
else:
d[-v-x] = 1
return list(res)
Leetcode成长记录,有些是自己的想法,有些是参考他人的,如有侵权,请予通知删去,谢谢!