leedcode: 523-连续的子数组和

261 阅读2分钟

每天做个总结吧,坚持就是胜利!

    /**
        @date 2021-06-02
        @description leedcode: 523-连续的子数组和
    */

壹(序)

出差在外,也不是来开发,所以今天并没有写代码,没有学习到新的知识点,所以解析一下leetcode的每日一题。 今天是连续的子数组和

贰(题目)

给你一个整数数组 nums 和一个整数 k ,编写一个函数来判断该数组是否含有同时满足下述条件的连续子数组:

子数组大小 至少为 2 ,且
子数组元素总和为 k 的倍数。
如果存在,返回 true ;否则,返回 false 。

如果存在一个整数 n ,令整数 x 符合 x = n * k ,则称 x 是 k 的一个倍数。

示例 1:

输入:nums = [23,2,4,6,7], k = 6
输出:true
解释:[2,4] 是一个大小为 2 的子数组,并且和为 6 。
示例 2:

输入:nums = [23,2,6,4,7], k = 6
输出:true
解释:[23, 2, 6, 4, 7] 是大小为 5 的子数组,并且和为 42 。 
42 是 6 的倍数,因为 42 = 7 * 67 是一个整数。
示例 3:

输入:nums = [23,2,6,4,7], k = 13
输出:false

叁(解题思路)

首先读明白题意,找到是否有一个长度为2及以上子数组,满足该子数组的和k的倍数

最先想到的是就是暴力解法,直接上循环,第一层循环是对数组每一项进行遍历,且定义一个sum,当做当前循环的数组和,然后由此处开始,往后递增,直到满足条件或此次循环结束,具体代码实现如下:

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {boolean}
 */
var checkSubarraySum = function (nums, k) {
    // 数组长度小于2,一定返回false
    if (nums.length <= 1) return false;
    
    // 第一层循环
    for (let i = 0; i < nums.length; i++) {
        // 定义此次循环的数组和sum,以及第二层循环需要截取的索引j
        let sum = nums[i], j = i + 1

        while (j < nums.length) {
            sum += nums[j]

            if (sum % k === 0) {
                return true
            } else {
                j++
            }
        }
    }
    
    // 循环结束都没有返回true,返回false
    return false
};

思路应该是没问题的,但是很可惜超时了,在数组很庞大的时候,这样的两层循环确实非常消耗性能,所以接下来需要进行优化

肆(优化)

一开始想优化的时候并没有思路,然后想起一次面试,面试官让我做两数之和,这道题很简单,就循环就完事了,但是当时让我优化时,我没有头绪,下来看别人的题解知道可以用hashMap

是的,这次优化也能使用hashMap:

将前面子数组的和与k的余数及其索引保存起来,再次遇到一样的余数时,将上一个余数之前的数字抛弃,只留下上次余数到此次的子数组,这时的子数组和就是k的倍数,需要注意还要满足子数组长度大于等于2

代码实现:

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {boolean}
 */
var checkSubarraySum = function (nums, k) {
    const len = nums.length
    
    if (len <= 1) return false;

    const map = {
        0: -1
    };

    let remainder = 0;

    for (let i = 0; i < len; i++) {
        // 更新余数
        remainder = (remainder + nums[i]) % k;
        // 判断是否有满足k的倍数的条件
        if (map[remainder] !== undefined) {
            // 判断是否满足子数组长度大于等于2的条件
            if (i - map[remainder] > 1) return true;
        } else {
            map[remainder] = i;
        }
    }
    // 遍历完都没有,返回false
    return false;
};

伍(实例分析)

输入:nums = [23,2,4,6,7], k = 6为例,进行hashMap解法的分析:

  1. 创建map,此时需要从第一项前面开始算起,余数为0,值给-1;

  2. 开始遍历数组,第一项是23,此时余数为5,map中并没有为5的属性,所以向map中插入5: 0

  3. 第二项是2,此时余数为1,map中并没有为1的属性,所以向map中插入1: 1

  4. 第三项是4,此时余数为5,map中有为5的属性,值为0,再看从1(0 + 1)到此处(2)的子数组长度是否满足大于等于2,2 - 0 = 2,所以满足,所以满足两个条件,返回true