摘要:连续子数组的性质想到通过前缀和,但是枚举前缀和需要平方级别的时间复杂度,在遍历得到前缀和的同时记住结果,可以使得时间复杂度降到线性时间复杂度。
题解 | 「力扣」第 525 题:连续数组
给定一个二进制数组 nums , 找到含有相同数量的 0 和 1 的最长连续子数组,并返回该子数组的长度。
示例 1:
输入: nums = [0,1]
输出: 2
说明: [0, 1] 是具有相同数量0和1的最长连续子数组。
示例 2:
输入: nums = [0,1,0]
输出: 2
说明: [0, 1] (或 [1, 0]) 是具有相同数量0和1的最长连续子数组。
提示:
1 <= nums.length <= 105nums[i]不是0就是1
思路分析:
- 首先想到「暴力解法」,使用两个
for循环枚举所有可能的区间,然后判断区间是否符合题意,进而计算区间的长度,选出最长; - 连续子区间的问题,可以考虑「滑动窗口」,但是这道题「滑动窗口」不适用,理由是:题目找的是最长,我们找到了一个包含 和 的个数相等的连续子区间以后,还需要考虑更长的连续子区间,右边界需要继续向右;
- 连续子区间的问题,可以考虑「前缀和」,前缀和的差是区间和。题目「包含 和 的个数相等」,可以把 换成 ,这样满足条件的区间和就可以定义为 。如果之前没有做过相关的问题,很难想到比较好的思路。这里我的思路来自「力扣」第 1371 题:每个元音包含偶数次的最长子字符串,周赛的时候没有做出来,然后看到了 题解 学到的技巧;
- 前缀和经常与哈希表结合使用,因为希望遍历得到前缀和的时候,一边遍历一边记住结果,以降低时间复杂度;
- 由于求的是 最长的连续子数组的长度,因此只需要记录前缀和的数值第 1 次出现的下标,相同的前缀再次出现,就说明这一段区间的和为 (把 看成 以后),在遍历的过程中,记录最长的区间的长度。
参考代码:
import java.util.HashMap;
import java.util.Map;
public class Solution {
public int findMaxLength(int[] nums) {
int len = nums.length;
Map<Integer, Integer> hashMap = new HashMap<>();
hashMap.put(0, -1);
int preSum = 0;
int res = 0;
for (int i = 0; i < len; i++) {
if (nums[i] == 0) {
preSum--;
} else {
preSum++;
}
if (!hashMap.containsKey(preSum)) {
hashMap.put(preSum, i);
} else {
// 因为求的是最长的长度,只记录前缀和第一次出现的下标,
// 注意:这里不需要加 1
res = Math.max(res, i - hashMap.get(preSum));
}
}
return res;
}
}
复杂度分析:
- 时间复杂度:,这里 是数组的长度,算法遍历了一次数组,每一个元素的操作都是常数次的,因此整体时间复杂度是 ;
- 空间复杂度:。
相关问题
大佬 @feeenedumplings 的题单:刷题交流|力扣题目列表-前缀和问题分类汇总 。