【首尾指针】leetcode求n数之和

  • 类型:缩减搜索空间,缩短时间复杂度
  • 方法:哈希表、首尾双指针

题目1:1. 两数之和

  • 题目:给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

  • 类型:无序查找用哈希表

  • 思路:01-记录所有数字的索引和值;02-找到num在nums中,target-num也在的情况,同时两者的索引不能相同。

  • 经验:(1)for遍历只能针对range或者enumerate对象,不能针对字典;(2)哈希表查找时间复杂度为O(n),如果是两层for循环时间复杂度为O(n^2)。 查询的方式是if value in hashmap

def twoSum(self, nums: List[int], target: int) -> List[int]:
    hashmap={}
    for index,value in enumerate(nums):
        hashmap[value]=index
    for index,value in enumerate(nums):  #字典不能用于遍历,要用enumerate函数
        if (target-value in hashmap) and (hashmap[target-value]!=index):
         return [index,hashmap[target-value]]

题目2:167. 两数之和 II - 输入有序数组

  • 题目:给定一个已按照 升序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。

  • 类型:有序查找用首尾双指针

  • 思路:01-初始化首尾;02-由于left和right分别向中间走,所以用while最方便;03-判断三种情况,只有找到目标才输出结果。

  • 经验:(1)首尾双指针法查找每次更新的步数是1步;(2)首尾双指针用while left<right作为循环结束条件。

def twoSum(self, numbers: List[int], target: int) -> List[int]:
    left,right=0,len(numbers)-1
    while left<right:
        sum_=numbers[left]+numbers[right]
        if sum_==target:return [left+1,right+1]
        elif sum_<target:left+=1
        else:right-=1

题目3:三数之和

  • 题目:给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
  • 类型:查找符合条件的三个数
  • 思路:01-排序+for+双指针的结构;02-for和双指针都要去重;03-循环前和循环后都要剪枝。
  • 经验:(1)单层for循环,break和return的作用一致;(2)当外层限制了left和right的关系,内层只更新left,一定要在内层的条件中加上left和right的关系,防止溢出。(3)每个指针都要求在这次循环能找到的组合中,针对这个指针来说,这次try的数和上次try的数不一样。
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        if not nums or len(nums)<3:return []
        res=[]
        nums=sorted(nums)
        #第一个数用for遍历
        for i in range(len(nums)):
            if i>0 and nums[i]==nums[i-1]:continue
            if nums[i]+nums[i+1]+nums[i+2]>0:break  #可以用break或者return两种方式来结束循环
            if nums[i]+nums[len(nums)-1]+nums[len(nums)-2]<0:continue
            #其他两个数用双指针
            left=i+1
            right=len(nums)-1
            while left<right:
                if (nums[i]+nums[left]+nums[right]==0):  #left和right之后都要更新,不能在上面就定义sum_
                    res.append([nums[i],nums[left],nums[right]])
                    #滑动窗口,只要不合法(相等),就一直滑动
                    while left<right and nums[left]==nums[left+1]:left+=1  #一定要加上left<right的限制!!!
                    while left<right and nums[right]==nums[right-1]:right-=1
                    left+=1
                    right-=1
                elif nums[i]+nums[left]+nums[right]<0:left+=1
                else:right-=1
        return res 

题目4:四数之和

  • 题目:给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (不重复指的是每个组合的唯一性)

  • 类型:查找符合条件的四个数

  • 思路:01-排序+两层for循环嵌套双指针的结构;02-循环和双指针中的去重;03-循环开始前以及每一层循环开始后有的剪枝;

  • 经验:(1)查找最一般用for循环,找多个数就嵌套多个for循环。但是可以用排序、指针代替双重循环、剪枝三种技巧来简化多数查找。也就是说,最后的呈现结构是for里面嵌套双指针;(2)continue指的是跳出本次循环,break指的是跳出本层循环,return指的是跳出所有循环;(3)剪枝指的是遇到某种情况就跳出本次/本层/所有循环。剪枝的思路:整个循环开始前以及每一层循环之后都可以剪枝
    a.空集或者长度不符合就直接输出空集
    b.遍历过程中最左边四个>target,跳出本层循环(i层/j层)
    c.遍历过程中最右边四个<target,跳出本次循环
    (4)去重复:
    a.for循环中:continue直到没有重复
    b.双指针中:while直到没有重复

    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        res=[]
        if not nums or len(nums)<4:return res #剪枝
        nums.sort()
        if nums[0]+nums[1]+nums[2]+nums[3]>target:return res 
        if nums[len(nums)-1]+nums[len(nums)-2]+nums[len(nums)-3]+nums[len(nums)-4]<target:return res#循环前剪枝
        for i in range(len(nums)-3):
            if i>0 and nums[i]==nums[i-1]:continue #不重复
            if nums[i]+nums[i+1]+nums[i+2]+nums[i+3]>target:break
            if nums[i]+nums[len(nums)-1]+nums[len(nums)-2]+nums[len(nums)-3]<target:continue#第一个数后剪枝
            
            for j in range(i+1,len(nums)-2):
                if j>i+1 and nums[j]==nums[j-1]:continue #不重复
                if nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target:break
                if nums[i] + nums[j] + nums[len(nums)-2] + nums[len(nums)-1] < target:continue
                
                left=j+1
                right=len(nums)-1
                while left<right:
                    if nums[i]+nums[j]+nums[left]+nums[right]==target:
                        res.append([nums[i],nums[j],nums[left],nums[right]])#找到了一个结果之后要继续找其他结果
                        while left<right and nums[left]==nums[left+1]:left+=1 #不重复
                        while left<right and nums[right]==nums[right-1]:right-=1 #不重复
                        left+=1
                        right-=1

                    elif nums[i]+nums[j]+nums[left]+nums[right]<target:left+=1
                    else:right-=1
        return res     

明日计划:
11. 盛最多水的容器:双指针
633. 平方数之和

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值