题解 | 「力扣」第 525 题:连续数组

227 阅读1分钟

摘要:连续子数组的性质想到通过前缀和,但是枚举前缀和需要平方级别的时间复杂度,在遍历得到前缀和的同时记住结果,可以使得时间复杂度降到线性时间复杂度。

题解 | 「力扣」第 525 题:连续数组

给定一个二进制数组 nums , 找到含有相同数量的 01 的最长连续子数组,并返回该子数组的长度。

示例 1:

输入: nums = [0,1]
输出: 2
说明: [0, 1] 是具有相同数量0和1的最长连续子数组。

示例 2:

输入: nums = [0,1,0]
输出: 2
说明: [0, 1] (或 [1, 0]) 是具有相同数量0和1的最长连续子数组。

提示:

  • 1 <= nums.length <= 105
  • nums[i] 不是 0 就是 1

思路分析

  • 首先想到「暴力解法」,使用两个 for 循环枚举所有可能的区间,然后判断区间是否符合题意,进而计算区间的长度,选出最长;
  • 连续子区间的问题,可以考虑「滑动窗口」,但是这道题「滑动窗口」不适用,理由是:题目找的是最长,我们找到了一个包含 0011 的个数相等的连续子区间以后,还需要考虑更长的连续子区间,右边界需要继续向右;
  • 连续子区间的问题,可以考虑「前缀和」,前缀和的差是区间和。题目「包含 0011 的个数相等」,可以把 00 换成 1-1,这样满足条件的区间和就可以定义为 00。如果之前没有做过相关的问题,很难想到比较好的思路。这里我的思路来自「力扣」第 1371 题:每个元音包含偶数次的最长子字符串,周赛的时候没有做出来,然后看到了 题解 学到的技巧;
  • 前缀和经常与哈希表结合使用,因为希望遍历得到前缀和的时候,一边遍历一边记住结果,以降低时间复杂度;
  • 由于求的是 最长的连续子数组的长度,因此只需要记录前缀和的数值第 1 次出现的下标,相同的前缀再次出现,就说明这一段区间的和为 00(把 00 看成 1-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;
    }
}

复杂度分析

  • 时间复杂度:O(N)O(N),这里 NN 是数组的长度,算法遍历了一次数组,每一个元素的操作都是常数次的,因此整体时间复杂度是 O(N)O(N)
  • 空间复杂度:O(N)O(N)

相关问题

大佬 @feeenedumplings 的题单:刷题交流|力扣题目列表-前缀和问题分类汇总