剑指offer 数组篇

文章探讨了解决子数组问题的常见方法,包括动态规划(DP)、滑窗和前缀和结合哈希的策略。针对和积最值和定值问题,提出了O(n)时间复杂度的解法,强调了滑窗和前缀和在特定问题中的应用,以及如何将0转换为-1来解决某些问题。文章强调了理解解题思路的重要性。
摘要由CSDN通过智能技术生成

常见子数组问题 通用解法
1. 和积 最值问题
第i位置的答案,一定由第i-1位置的答案发展而来,不用考虑i-2, i-3...
因此用普通dp,可以做到O(n)。

53. 最大子数组和
普通DP。因为它向前扩展必连上dp[i-1],考虑dp[i-2]的话就不是连续子数组了。
有一个更优解法判断 sum 是否大于0的,其实就是化简后的普通DP。
152. 乘积最大子数组
可普通DP,同上。只不过是状态机DP。
2. 和积 定值问题
2.1 同向滑窗
双指针,以右边界为循环重心,每次+1;不满足条件时左边界收缩。
要求:右指针右移 和 左指针右移 效果相反。这样收缩左边界才可以抵消扩张右边界带来的作用。

剑指 Offer II 008. 和大于等于 target 的最短子数组
实用建议:推荐 建立窗口 和 移动窗口 写在一个循环中。不推荐滑窗先写个循环建立窗口,再写个循环向右滑动。建立窗口循环末尾容易漏掉这种情况:窗口盛满序列时,还需要移动窗口左边缘以达到最优解。
2.2 前缀和 + 哈希

前缀和需多增加一个初识值,长度+1。


前缀和数组,把子数组和问题转化为两数之差(类似 两数之和)问题。用哈希就欧了。

区别是hash存储时一个存前缀和下标,一个存储个数。

560. 和为 K 的子数组 (这里求和为k的个数,非最大长度)
这个题很多人直接滑窗然后错了。如果数据中没有负数,才能用滑窗。因为全正数的话,右指针右移 和 左指针右移 效果相反:前者使sum增加,后者使sum减小。
0 和 1 个数相同的子数组  (这里求最大长度,非个数)
这个题题解没人说为什么不能滑窗。但其实新手很容易想到滑窗,然后无法解决。一样的道理:左指针右移 和 右指针右移,并不能保证效果相反。右指针右移,可能导致增加一个0;此时收缩窗口,右移左指针,却不能保证减少一个0或增加一个1。
现在,我们确定不能滑窗。题解大多数上来就说把0换成-1即可,没有人说怎么想到这个思路。这个思路有没有一个通用的方法能让我们想出来呢?其实也是有的。横线下方即本题题解。进入题解之前,我们先思考一些有趣的内容,这有助于我们将方法迁移到更多的题目上。
2.3 一些思考
DP问题有两种很典型的问题:子数组和子序列。

求子数组时,一般是O(n),因为我们求dp[i]时,只用考虑dp[i-1]。再往前就没法连续了。
求子序列时,一般是O(n^2),我们需要考虑所有dp[j] (j<i)。如 300. 最长递增子序列
那么:为什么上面提到了 “同向滑窗” 和 “前缀和 + 哈希”,有办法把O(n^2)降低到O(n)?(前提:题目还是求子数组。。如果是子序列,爱莫能助)

前缀和:

用O(1)综合了前N个数的特征(有状态压缩的思想)。
虽然只是求和,我们不能像真正状态压缩一样保留前n个数的全部数据,相当于只保留了“和”这一部分特征。
但当问题与我们所保留的特征相符时,即可完美从O(N)转化为O(1)。那么,什么类型的问题能这么做呢?那就看上文即可明白,什么时候用同向滑窗,什么时候用前缀和 + 哈希。 这也是本文的目的:追溯思路的起因。怎么做重要,怎么想到这么做更重要。
同向滑窗:

假设右边界为遍历的重心,每次扩大右边界时,检查条件,如果不符,左边界收缩。
右边界不同时,左边界是连续收缩的。这就是滑动窗口从O(N)转化为O(1)的核心。
同理,一些题目中用到反向滑窗(如快速排序,反向双指针比同向更容易理解,且不容易错)。能用反向滑窗的前提条件是:左指针右移 和 右指针左移 效果相反。并且反向滑窗能把O(N)降低为O(1)的核心也是:假设左边界看作循环的重心,那么右边界收缩是连续的,并不用每次都从nums.length-1出发向左一一检查。
本题题解:为什么要把0都变成-1?
1. 尝试前缀和
根据本文,我们只能用前缀和 + 哈希了。
题目求的是个数,我们就先尝试组出0和1的个数的前缀和数组。
前缀和其实是有状态压缩的思想在里面,用O(1)的空间保存了O(n)的数据,只不过只保存了和。这也是我们这么尝试的理由。
2. 求差
题目求个数差为0,我们就对0和1的个数的前缀和数组求差,就可以发现:把 所有差相同的地方作为端点,这一段内部的个数差都为0,也就是 0 和 1 的个数相等。
道理也很容易想明白:从第0位开始数,到第i位时,0比1多3个;到第j位时,0也比1多3个,他们中间的个数必然相等。
3. 写代码
由此可以开始写代码。
上面步骤很多数组可以简化为一个变量。最终代码就是和 “把0转化为-1”是一样的。
之所以转化为 -“1”,因为 1 正好是用于计数,并且可以和 +1 做抵消罢了。
链接:https://leetcode.cn/problems/A1NYOS/solution/chang-jian-zi-shu-zu-wen-ti-tong-yong-ji-v0n4/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值