LeetCode Day2

刷题日记

刚刚告别第一份工作,充电ing,从今天开始化身老蒋
ps:菜鸡一个,不一定是自己想到的思路,只是自己理解后做个记录

leetCode 523.连续的子数组和

给你一个整数数组 nums 和一个整数 k ,编写一个函数来判断该数组是否含有同时满足下述条件的连续子数组:
	● 子数组大小 至少为 2 ,且
	● 子数组元素总和为 k 的倍数。
如果存在,返回 true ;否则,返回 false 。
如果存在一个整数 n ,令整数 x 符合 x = n * k ,则称 x 是 k 的一个倍数。0 始终视为 k 的一个倍数。

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

提示:
	● 1 <= nums.length <= 10^50 <= nums[i] <= 10^90 <= sum(nums[i]) <= 2^31 - 11 <= k <= 2^31 - 1
  
思路:前缀和,同余定理
首先看到 "连续子数组(序列)和" 这几个字眼,我们第一时间想到的就是前缀和,我们用sums来存放数组的前缀和。
以示例为例:
nums = [23,2,4,6,7],那么对应的
sums = [23,25,29,35,42]
按照前缀和的一般思路,假设存在满足条件的连续子数组(数组和为k的倍数),即sums[i] - sums[j] = n * k (j < i)成立。
那么我们就能通过判断 sums[j] = sums[i] -  n * k 是否存在来得出结论,但是这里的 n * k 并不是一个固定值,这种思路显然不行。

而且注意到给定的取值范围 nums.length <= 10^5,且要求子数组的长度至少为2。如果我们通过双循环来遍历前缀和的差值来获取所有子数组,然后再通过 差值 % k == 0 来判断,时间复杂度为n^2,数据量较大时会超时。
   public static boolean checkSubarraySum1(int[] nums, int k) {
        int length = nums.length;
        if (length == 1) {
            return false;
        } else {
            boolean result = false;
            int[] sums = new int[nums.length + 1];
            sums[1] = nums[0];
            for (int i = 1; i < nums.length; i++) {
                sums[i + 1] = sums[i] + nums[i];
                for (int j = 0; j <= (i + 1) - 2; j++) {
                    if ((sums[i + 1] - sums[j]) % k == 0) {
                        result = true;
                        break;
                    }
                }
                if (result) {
                    break;
                }
            }
            return result;
        }
    }

所以我们必须找到这么一个值,能直接判断两数之差 是否能整除 k,这时候就想到了同余定理(数学知识,所以说学好数理化是有道理的。)
同余定理大概定义: (a - b) % k = 0	---->	a % k == b % k (ps:具体证明网上很多,这里就不多赘述了)

代码:
	public static boolean checkSubarraySum(int[] nums, int k) {
        int length = nums.length;
        if (length == 1)
            return false;
        else {
            int sums = 0;
            boolean result = false;
            HashMap<Integer, Integer> map = new HashMap<>();	//key: 前缀和 % k的值 value:i
            map.put(0, 0);							//插入一个0是为了处理sums[i]本身正好满足条件。以示例输入为例:
            										//sums.length = nums.length + 1
            										//[0,23,25,29,35,42]
            for (int i = 1; i <= length; i++) {
                sums += nums[i - 1];
                int key = sums % k;
                if (map.containsKey(key)) {
                    if (i - map.get(key) > 1) {		//解决题目另外一个条件,要求子数组长度至少为2
                    								//i - map.get(key) = 1 则说明nums[i]为k的倍数,子数组长度为1
                    								//此时也无需将此相邻重复key插入,防止出现[...,6,6,..]这样的情况
                        result = true;				
                        break;
                    }								
                }
                else {
                    map.put(key, i);
                }
            }
            return result;
        }
    } 

前缀和参考文章:前缀和系列
同余定理参考文章:同余定理

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值