文章目录
1. 题目信息
1.1 题目描述
题目链接: 525. 连续数组
给定一个二进制数组 nums , 找到含有相同数量的 0 和 1 的最长连续子数组,并返回该子数组的长度。
1.2 测试用例
示例 1:
输入: nums = [0,1]
输出: 2
说明: [0, 1] 是具有相同数量0和1的最长连续子数组。
- 示例 2:
输入: nums = [0,1,0]
输出: 2
说明: [0, 1] (或 [1, 0]) 是具有相同数量0和1的最长连续子数组。
- 示例 3:
输入: nums =[0,1,0,0,1,0,0,1,0,0,1,0]
输出: 4
- 提示:
1 <= nums.length <= 105
nums[i] 不是 0 就是 1
2. 题目分析
说明, 今天的每日一题, 与昨天的有这惊人的相似; 建议先阅读一下昨天的题解:
2.1 前缀和+哈希存0/1出现的次数cnt
根据题意, 很容易想到前缀和来减少循环次数, 那我们这里的前缀和是对前面遇到的0、1进行求和吗?
假设一下, 如果是用前缀求和, 那么这个前缀和只能提现1的个数, 没法提现0的个数, 如果要判定两个下标区间的值是否满足要求, 就必须用公式 ( j − i + 1 ) = = 2 ∗ ( p r e v C n t I n d e x [ j ] − p r e v C n t I n d e x [ i ] ) (j-i+1) == 2 * (prevCntIndex[j]-prevCntIndex[i]) (j−i+1)==2∗(prevCntIndex[j]−prevCntIndex[i]) 来判定, 其中
i < j
, 这样还是需要两层循环遍历 O ( N 2 ) O(N^2) O(N2) ;
所以得想办法找个简单的值来标记是否中间的区间满足要求, 类似于 523 题目中的记录 余数的思想;
前缀和
转换, 由于0、1需要配对, 所以可以用一个计数器cnt, 来标记0、1是否匹配;遇到 0 时 cnt--
;遇到 1 时 cnt++
;
- 哈希优化将每个新的cnt出现的位置存放到map中区,利用贪心思想只存首次出现的位置, 这样找出来的子数组就最长了;;
举例如下:
- 给定一个数组ns=[0,1,0,0,1,0,0,1,0,0,1,0], 我们计算出来的cnt数组的关系为:
ns = [ 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0]
cs = [-1, 0,-1,-2,-1,-2,-3,-2,-3,-4,-3,-4]
- 则根据cs计数相同的位置求差可以计算出子数组的长度, 比如: cs[0] == cs[4] == -1, 长度为4, 对应原始数组中的子数组为
[1, 0, 0, 1]
3. 代码详情
3.1 C++
3.1.1 前缀和+哈希存存0/1出现的次数cnt
// 执行用时:128 ms, 在所有 C++ 提交中击败了89.88%的用户
// 内存消耗:81.7 MB, 在所有 C++ 提交中击败了31.25%的用户
class Solution {
public:
int findMaxLength(vector<int>& nums) {
int n = nums.size();
unordered_map<int, int> prevCntIndex;
prevCntIndex[0] = -1;
int cnt = 0;
int ans = 0;
for (int i = 0; i < n; i++) {
if (nums[i]) {
cnt++;
} else {
cnt--;
}
if (prevCntIndex.count(cnt)) {
ans = max(ans, i - prevCntIndex[cnt]);
} else {
prevCntIndex[cnt] = i;
}
}
return ans;
}
};
3.2 Python
- 与上面C++思路相同;
# 执行用时:212 ms, 在所有 Python3 提交中击败了98.70%的用户
# 内存消耗:19.6 MB, 在所有 Python3 提交中击败了28.57%的用户
class Solution:
def findMaxLength(self, nums: List[int]) -> int:
cnt = 0
prevCntIndex = {0: -1}
ans = 0
for i, n in enumerate(nums):
if n:
cnt += 1
else:
cnt -= 1
if cnt not in prevCntIndex:
prevCntIndex[cnt] = i
else:
ans = max(ans, i - prevCntIndex[cnt])
return ans