leetcode No525 连续数组 java

题目

给定一个二进制数组 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 <= 105
nums[i] 不是 0 就是 1

来源:力扣(LeetCode)
链接:No525 连续数组

分析

略加思索就可以发现这道题和我昨天说的一道题非常相似,都是使用 前缀和+哈希表解决问题
如果对上一道题感兴趣的可以看看这一篇题解

—题目leetcode No.523 连续的子数组和
—题解leetcode No.523 连续的子数组和 java

为什么这两道题相似呢,我们来看:
要求0 和 1两个数字在一段连续子数组中数量相等,意思就是说0的数量等于1的数量,我们转念一想,把0换成-1,那这段子数组之和不就等于0了吗,这样我们就巧妙地将题目变换为前一道题了

下面放代码:

class Solution {
    public int findMaxLength(int[] nums) {
        int len = nums.length;
        int[] sum = new int[len + 1];
        for (int i = 1; i <= len; i++) {
            if (nums[i - 1] == 0)
                sum[i] = sum[i - 1] - 1;
            else
                sum[i] = sum[i - 1] + 1;
        }
        Map<Integer, Integer> map = new HashMap<>();
        int max = 0;
        for (int i = 2; i <= len; i++) {
            if (!map.containsKey(sum[i - 2])) map.put(sum[i - 2], i - 2);
            if (map.containsKey(sum[i])) {
                max = Math.max(max, i - map.get(sum[i]));
            }
        }
        return max;
    }
}

因为返回值是子数组的长度,因此我使用了一个map来存储数据,这里有几个要注意的小细节:

  1. map保存的数据,key是sum元素值,value是sum下标
  2. 要有重复判断:if (!map.containsKey(sum[i - 2])) …
  3. 使用max记录子数组最大长度

这是一种朴素的做法,其效率只能说是一般
时间复杂度:O(n)
空间复杂度:O(n)


其实我们可以像官方题解一样,将开辟sum数组的空间省略掉,使用一个count动态的累加nums数组来判断。

代码如下:

class Solution {
    public int findMaxLength(int[] nums) {
        int maxLength = 0;
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        int counter = 0;
        map.put(counter, -1);
        int n = nums.length;
        for (int i = 0; i < n; i++) {
            int num = nums[i];
            if (num == 1) {
                counter++;
            } else {
                counter--;
            }
            if (map.containsKey(counter)) {
                int prevIndex = map.get(counter);
                maxLength = Math.max(maxLength, i - prevIndex);
            } else {
                map.put(counter, i);
            }
        }
        return maxLength;
    }
}

还有一种比较nb的做法,这是参考了别人的优秀代码来分析的:

class Solution {
    public int findMaxLength(int[] nums) {
        int[] cnt = new int[2*nums.length + 1];
        Arrays.fill(cnt, -1);
        int count = 0;
        int max = 0;
        for(int i = 0; i < nums.length; i++) {
            count += (nums[i] == 0? 1 : -1);
            if(count == 0) max = i + 1; // 从开头到这是最大的
            else if(cnt[count + nums.length] != -1) {
                max = Math.max(max, i - cnt[count + nums.length]);
            } else {
                cnt[count + nums.length] = i;
            }
        }
        return max;
    }
}
  1. 首先,它的总体思路没错,都是使用了前缀和进行解题,但是没有使用哈希表,因此速度快很多。
  2. 其次,它使用count的思路和官方题解的count用法是一样的,都是由于动态计算nums前i个数总和。
  3. 最核心的不同,就是它使用的数组代替哈希表,因为count有正有负,因此我们不以0为基准,而是以nums.length为基准,因为count最大能到nums.length - 1,最小也能到 -nums.length + 1,因此在功能上完美代替了哈希表。
if(count == 0) max = i + 1; // 从开头到这是最大的
else if(cnt[count + nums.length] != -1) {
     max = Math.max(max, i - cnt[count + nums.length]);
 } else {
     cnt[count + nums.length] = i;
 }

这一段就是最核心的代码,要理解透彻这一部分。


总结

一道题目有很多的优化方法,本题是leetcodeNo523的变种,使用的都是前缀和+哈希表,在优化方案中,可以做如下优化:

  1. 省去开前缀和数组的空间。
  2. 可以不使用哈希表。

有任何想补充的欢迎补充,喜欢的看客们点个赞吧~

doge

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值