【算法】剑指 Offer 专项突击版 Day3数组部分

【算法】剑指 Offer 专项突击版 Day3数组部分

题目地址https://leetcode-cn.com/study-plan/lcof/?progress=wgzvtig

目标:要点总结,分享思路
通俗代码,满足要求快速实现



I 【中等】007. 数组中和为 0 的三个数

在这里插入图片描述
难度:普通,双指针题

要点

  • 思路:数组排序后,在不重复要求下,设置起始第一个数base,在剩下数字中通过双指针寻找和为target-base的两个数
  • 重点:为什么一定先对数组排序,因为只有排序后才能使用双指针。且先不排序的话,循环中每次寻找也是要排序的,故排序是必需的。(可以先通过暴力遍历思考理解)
               这道题想到双指针就可以做出来了,但是加入一些剪枝可以大大降低时间复杂度

          剪枝1:如果第一个数字已经成功使用过,就不需要双指针重复找剩下两个数
          剪枝2:在有序数组条件下,如果两数最值都不满足target直接更换base进入下一轮寻找

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        if len(nums)<3:return []
        ans,ans_set,first_num_set = [],set(),set()
        nums.sort()

        for i in range(len(nums)-2):
            base = nums[i] # 第一个数字
            target = 0-base 
            if base not in first_num_set: # 剪枝1
                left,right = i+1,len(nums)-1
                if nums[left]+nums[left+1]>target or nums[right] + nums[right-1]<target:
                    continue  # 剪枝2
                while left<right:
                    if nums[left]+nums[right] < target:
                        left+=1
                    elif nums[left]+nums[right]>target:
                        right-=1
                    else:
                        if (base,nums[left],nums[right]) not in ans_set:
                            ans.append([base,nums[left],nums[right]])
                            ans_set.add((base,nums[left],nums[right]))
                            first_num_set.add(base)
                        left+=1
                        
        return ans

总结:两数之和、三数之和等问题可以考虑双指针+剪枝解决

II 【中等】 008. 和大于等于 target 的最短子数组

在这里插入图片描述

难度:普通,滑动窗口题

要点:两种方法

  • 思路:(1)设置双指针,右指针不断向右移,当窗口和>目标值时记录窗口长度并不断移动左指针,时间复杂度 O ( n ) O(n) O(n)
  •    (2)前缀和+二分查找,因题目中明确数组中均为正数,这样就满足了前缀和递增有序,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn),题目有这个思考要求,理解就好
  • 小技巧:这种最小值长度问题可以先将结果ans设置成数组长度+1,这样后面判断一下就知道ans是否有改变,因为满足条件的最大值长度就是数组长度

滑动窗口版

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        # 滑动窗口——推荐
        if not nums:return 0
        left,right=0,0
        total = 0
        ans = len(nums)+1 # 设置成最大长度+1
        while right<len(nums):
            total+=nums[right]
            while total>=target:
                ans = min(ans,right-left+1)
                total -= nums[left]
                left  += 1
            right+=1
        return ans if ans!=len(nums)+1 else 0

前缀和+滑动窗口

图例全部详细请见
【花落&月缺】反正我也没弄白前缀和方法哪里比较好……(T_T)(前缀和、滑动窗口)
这里截一张他的图便于理解
在这里插入图片描述

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        # 前缀和 + 二分查找
        sums = [0]
        ans = len(nums)+1
        for i in range(len(nums)):
            sums.append(sums[-1]+nums[i])
        
        for i in range(1,len(sums)):
            find = target+sums[i-1]
            middle = bisect.bisect_left(sums,find)
            if middle!=len(sums):  
                ans = min(middle-i+1,ans)
        return ans if ans!=len(nums)+1 else 0

III 【中等】009. 乘积小于 K 的子数组

在这里插入图片描述
难度:简单的滑动窗口题

要点

  • 思路:双指针解决滑动窗口问题,窗口不断向右移动,即右指针不断增加,当窗口内乘积大于等于目标k时,缩小窗口,即左指针不断减小
class Solution:
    def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:
        if k<=1: return 0 
        left,right,ans,multi = 0,0,0,1
        while right<len(nums):
            multi *= nums[right]
            while multi >= k:
                multi /= nums[left]
                left+=1
            ans += right - left+1
            right += 1
        return ans
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿芒Aris

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值