LeetCode-Arrary-TwoSum以及ThreeSum

本文详细解析了TwoSum与ThreeSum问题的多种解法,包括暴力解法、哈希表方法及双指针技巧,并对不同方法的时间与空间复杂度进行了对比。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、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 abc 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成长记录,有些是自己的想法,有些是参考他人的,如有侵权,请予通知删去,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值