前缀和+哈希表,剑指 Offer II 010. 和为 k 的子数组、剑指 Offer II 011. 0 和 1 个数相同的子数组

剑指 Offer II 010. 和为 k 的子数组

给定一个整数数组和一个整数 k ,请找到该数组中和为 k 的连续子数组的个数。

示例 1:
输入:nums = [1,1,1], k = 2
输出: 2
解释: 此题 [1,1] 与 [1,1] 为两种不同的情况

示例 2:
输入:nums = [1,2,3], k = 3
输出: 2

提示:
1 <= nums.length <= 2 * 104
-1000 <= nums[i] <= 1000
-107 <= k <= 107

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/QTMn0o
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路及方法

最初想的是滑动窗口、或者暴力,后面觉得前缀和貌似效率更高。

我们并不需要构建一个数组来记录前缀和,只用维护一个变量sum,遍历数组时对sum进行更新,同时用一个map记录sum出现的次数。类似于力扣第一题两数之和,我们每次遍历时,都判断map中是否存在sum-k,存在则说明上一次出现sum的下标到遍历本次时的连续子数组和为k,记录出现sum-k的次数即可。

class Solution {
    public int subarraySum(int[] nums, int k) {
        // 前缀和,map记录前i个数和以及出现次数
        Map map = new HashMap<>();
        map.put(0, 1);

        int res = 0;
        // 记录前缀和
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
            // map存在sum - k,及说明存在i之前某一位置到i连续子数组和为k
            if (map.containsKey(sum - k)) {
                res += map.get(sum - k);
            }
            map.put(sum, map.getOrDefault(sum, 0) + 1);
        }

        return res;
    }
}

结果如下:

剑指 Offer II 011. 0 和 1 个数相同的子数组

给定一个二进制数组 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)
链接:https://leetcode.cn/problems/A1NYOS
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路及方法

和上一道题一样,也是前缀和+哈希表,注释已经写的很清楚了。这里说一下初始入map,没有元素时前缀和为0,但它的下边为-1,是为了后续计算子数组长度时用当前下标减去-1即可,就不用像计算普通长度时是j - i + 1。

用变量sum记录前缀和,遇见1加1,遇见0减1,这样保证下一次当前缀和为sum且map中存在sum时,构成的子数组经过+相同数量的1和-相同数量的1,则代表子数组中有相同数量的0和1,对比长度即可。

class Solution {
    public int findMaxLength(int[] nums) {
        int res = 0;
        // 记录前缀和
        int sum = 0;
        // 记录前缀和及下标
        Map map = new HashMap<>();
        // 记录初始前缀和
        map.put(0, -1);

        for (int i = 0; i < nums.length; i++) {
            int num = nums[i];
            // 为1加1,为0减1
            if (num == 1) sum++;
            else sum--;

            // 如果map包含sum,及证明第一次出现sum的下标到此时sum组成的连续子数组包含相同0和1
            if (map.containsKey(sum)) {
                res = Math.max(res, i - map.get(sum));
            } else {
                // 仅记录第一次前缀和出现的下标,不更新后续出现位置
                map.put(sum, i);
            }
        }
        
        return res;
    }
}

结果如下:

你可能感兴趣的:(前缀和+哈希表,剑指 Offer II 010. 和为 k 的子数组、剑指 Offer II 011. 0 和 1 个数相同的子数组)