0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

LeetCode 560:和为K的子数组

算法与数据结构 来源:吴师兄学算法 作者:吴师兄学算法 2022-08-02 14:17 次阅读
大家好,我是吴师兄,

今天的题目来源于 LeetCode 第 560 号问题:和为 K 的子数组,难度为「中等」。

一、题目描述

给你一个整数数组nums和一个整数k,请你统计并返回该数组中和为k的子数组的个数

示例 1:

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

示例 2:

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

提示:

  • 1 <= nums.length <= 2 * 10^4
  • -1000 <= nums[i] <= 1000
  • -10^7 <= k <= 10^7

二、题目解析

补充知识点前缀和:前缀和指一个数组的某下标之前的所有数组元素的和(包含其自身)。

利用前缀和这种特点,可以快速的计算某个区间内的和,比如前 i 个元素的前缀和为preSum[i] = num[0] + nums[1] + ... + nums[i],而前 j 个元素的前缀和为preSum[j] = num[0] + nums[1] + ... + nums[j]

那么区间[ i , j ]之间的子数组之和就是 **preSum[j] - preSum[i]**。

81fa901c-1217-11ed-ba43-dac502259ad0.png

基于这种思路,可以先遍历一次数组,求出前缀和数组。

82155be0-1217-11ed-ba43-dac502259ad0.png

题目这个时候就变成了需要寻找出多少个 i 和 j 的组合,使得 [ i , j ] 这个区间的和为 k

classSolution{
publicintsubarraySum(int[]nums,intk){

intlen=nums.length;

int[]preSum=newint[len+1];

preSum[0]=0;

for(inti=0;i< len; i++) {
            preSum[i + 1]=preSum[i]+nums[i];
}

intcount=0;

for(inti=0;i< len; i++) {
          
            for(intj=i;j< len; j++) {
              
                if(preSum[j+1]-preSum[i]==k){
count++;
}
}
}
returncount;
}
}

在计算过程中,有两个 for 循环发生了嵌套,时间复杂度来到了 O(n^2) 级别。

需要优化

事实上,我们不需要去计算出具体是哪两项的前缀和之差等于k,只需要知道等于 k 的前缀和之差出现的次数 count,所以我们可以在遍历数组过程中,先去计算以 nums[i] 结尾的前缀和 pre,然后再去判断之前有没有存储 pre - k 这种前缀和,如果有,那么 pre - k 到 pre 这中间的元素和就是 k 了。

具体操作如下:

1、利用哈希表,以前缀和为键,出现次数为对应的值,记录 pre[i] 出现的次数。

2、开始从头到尾遍历 nums 数组,在遍历过程中,会执行两个操作。

3、存储索引为 i 的这个元素时,前缀和的值是多少,并且把这个值出现的频次存储到 mp 中。

823da2e4-1217-11ed-ba43-dac502259ad0.png

4、判断之前有没有存储 pre - k 这种前缀和,如果有,说明 pre - k 到 pre 直接的那些元素值之和就是 k。

5、返回结果。

三、参考代码

1、Java 代码

//登录AlgoMooc官网获取更多算法图解
//https://www.algomooc.com
//作者:程序员吴师兄
//代码有看不懂的地方一定要私聊咨询吴师兄呀
//和为 K 的子数组(LeetCode 560):https://leetcode.cn/problems/subarray-sum-equals-k/
classSolution{
publicintsubarraySum(int[]nums,intk){

//统计和为K的子数组的数量
intcount=0;

//记录遍历到索引为i的这个元素时,前缀和的值是多少
intpre=0;

//利用哈希表,以前缀和为键,出现次数为对应的值,记录pre[i]出现的次数
HashMapmp=newHashMap<>();

//一开始,需要设置前缀和为0时,出现的次数为1次
//这一行的作用就是为了应对nums[0]+nums[1]+...+nums[i]==k这种情况
//如数组[1,2,3,6]
//这个数组的累加和数组为[1,3,【6】,12]
//如果k=6,假如mp中没有预先存储(0,1)
//那么来到累加和为6的位置时,这时mp中存储的就只有两个数据(1,1),(3,1)
//想去判断mp.containsKey(pre-k),这时pre-k=6-6=0
//但map中没有(0,1),
//因为这个时候忽略了从下标0累加到下标i等于k的情况
//仅仅是统计了从下标大于0到某个位置等于k的所有答案
mp.put(0,1);

//开始从头到尾遍历nums数组,在遍历过程中,会执行两个操作
//1、存储索引为i的这个元素时,前缀和的值是多少,并且把这个值出现的频次存储到mp中
//2、判断之前有没有存储pre-k这种前缀和,如果有,说明pre-k到pre直接的那些元素值之和就是k
for(inti=0;i< nums.length; i++) {

            //存储索引为i的这个元素时,前缀和的值是多少
pre+=nums[i];

//判断之前有没有存储pre-k这种前缀和
if(mp.containsKey(pre-k)){

//如果有,说明pre-k到pre直接的那些元素值之和就是k
//找到了一组,累加到count上
count+=mp.get(pre-k);

}

//这个值出现的频次存储到mp中
// getOrDefault:当 Map 集合中有这个 key 时,就使用这个 key 对应的 value 值
//如果没有就使用默认值defaultValue
mp.put(pre,mp.getOrDefault(pre,0)+1);
}

//返回结果
returncount;
}
}

2、C++ 代码

classSolution{
public:
intsubarraySum(vector<int>&nums,intk){

//统计和为K的子数组的数量
intcount=0;

//记录遍历到索引为i的这个元素时,前缀和的值是多少
intpre=0;

//利用哈希表,以前缀和为键,出现次数为对应的值,记录pre[i]出现的次数
unordered_map<int,int>mp;

//一开始,需要设置前缀和为0时,出现的次数为1次
//这一行的作用就是为了应对nums[0]+nums[1]+...+nums[i]==k这种情况
//如数组[1,2,3,6]
//这个数组的累加和数组为[1,3,【6】,12]
//如果k=6,假如mp中没有预先存储(0,1)
//那么来到累加和为6的位置时,这时mp中存储的就只有两个数据(1,1),(3,1)
//想去判断mp.containsKey(pre-k),这时pre-k=6-6=0
//但map中没有(0,1),
//因为这个时候忽略了从下标0累加到下标i等于k的情况
//仅仅是统计了从下标大于0到某个位置等于k的所有答案
mp[0]=1;

//开始从头到尾遍历nums数组,在遍历过程中,会执行两个操作
//1、存储索引为i的这个元素时,前缀和的值是多少,并且把这个值出现的频次存储到mp中
//2、判断之前有没有存储pre-k这种前缀和,如果有,说明pre-k到pre直接的那些元素值之和就是k
for(inti=0;i< nums.size(); i++) {

            //存储索引为i的这个元素时,前缀和的值是多少
pre+=nums[i];

//判断之前有没有存储pre-k这种前缀和
if(mp.find(pre-k)!=mp.end()){

//如果有,说明pre-k到pre直接的那些元素值之和就是k
//找到了一组,累加到count上
count+=mp[pre-k];

}

//这个值出现的频次存储到mp中
mp[pre]++;
}

//返回结果
returncount;

}
};

3、Python 代码

classSolution:
defsubarraySum(self,nums:List[int],k:int)->int:
#统计和为K的子数组的数量
count=0

#记录遍历到索引为i的这个元素时,前缀和的值是多少
pre=0

#利用哈希表,以前缀和为键,出现次数为对应的值,记录pre[i]出现的次数
mp=collections.defaultdict(int)

#一开始,需要设置前缀和为0时,出现的次数为1次
#这一行的作用就是为了应对nums[0]+nums[1]+...+nums[i]==k这种情况
#如数组[1,2,3,6]
#这个数组的累加和数组为[1,3,【6】,12]
#如果k=6,假如mp中没有预先存储(0,1)
#那么来到累加和为6的位置时,这时mp中存储的就只有两个数据(1,1),(3,1)
#想去判断mp.containsKey(pre-k),这时pre-k=6-6=0
#但map中没有(0,1),
#因为这个时候忽略了从下标0累加到下标i等于k的情况
#仅仅是统计了从下标大于0到某个位置等于k的所有答案
mp[0]=1

#开始从头到尾遍历nums数组,在遍历过程中,会执行两个操作
#1、存储索引为i的这个元素时,前缀和的值是多少,并且把这个值出现的频次存储到mp中
#2、判断之前有没有存储pre-k这种前缀和,如果有,说明pre-k到pre直接的那些元素值之和就是k
foriinrange(len(nums)):

#存储索引为i的这个元素时,前缀和的值是多少
pre+=nums[i]

#判断之前有没有存储pre-k这种前缀和
#如果有,说明pre-k到pre直接的那些元素值之和就是k
#找到了一组,累加到count上
#利用defaultdict的特性,当presum-k不存在时,返回的是0
count+=mp[pre-k]

#这个值出现的频次存储到mp中
# getOrDefault:当 Map 集合中有这个 key 时,就使用这个 key 对应的 value 值
#如果没有就使用默认值defaultValue
mp[pre]+=1

#返回结果
returncount

四、复杂度分析

时间复杂度:O(n),其中 n 为数组的长度。我们遍历数组的时间复杂度为 O(n),中间利用哈希表查询删除的复杂度均为 O(1),因此总时间复杂度为 O(n)。

空间复杂度:O(n),其中 n 为数组的长度。哈希表在最坏情况下可能有 n 个不同的键值,因此需要 O(n) 的空间复杂度。

审核编辑 :李倩


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数组
    +关注

    关注

    1

    文章

    409

    浏览量

    25612
  • leetcode
    +关注

    关注

    0

    文章

    20

    浏览量

    2305

原文标题:LeetCode 560:和为 K 的子数组

文章出处:【微信号:TheAlgorithm,微信公众号:算法与数据结构】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    深入探索KUKA KRL中的数组应用

    如果 CHAR 类型数组的所有数组元素都拥有相同的字符串,则不必单独初始化每个数组元素。忽略右侧的数组下标。(对于一维数组下标,不写下标。)
    的头像 发表于 04-18 10:37 165次阅读
    深入探索KUKA KRL中的<b class='flag-5'>数组</b>应用

    PHP中数组的使用方法!

    PHP中数组的使用方法! PHP是一种广泛使用的网络编程语言,它的数组功能非常强大且灵活。数组是一种数据结构,它允许我们在单个变量中存储多个值。 在本篇文章中,我将详细解释PHP数组
    的头像 发表于 01-12 15:11 183次阅读

    数组和指针不相同吗?数组和指针有哪些区别

    数组就是指针,指针就是数组,这样的言论在评论区看到不下于10次。
    的头像 发表于 12-13 16:34 363次阅读
    <b class='flag-5'>数组</b>和指针不相同吗?<b class='flag-5'>数组</b>和指针有哪些区别

    数组与指针不能混用的情况

    数组与指针不能混用的情况  数组与指针是 C/C++ 中非常常见的特性和概念。然而,在某些情况下,数组与指针是不能混用的。这种情况通常涉及到数组的内存分配和
    的头像 发表于 12-07 13:46 321次阅读

    C语言中数组的用法

    C语言的数组是一种数据结构,它可以存储多个相同类型的数据,例如整数,字符,浮点数等。数组的每个元素都有一个索引,用来表示它在数组中的位置。数组的索引从0开始,也就是说,
    的头像 发表于 11-24 17:48 863次阅读
    C语言中<b class='flag-5'>数组</b>的用法

    c语言中数组怎么定义

    C语言中,数组是一种用来存储相同类型元素的数据结构。它可以存储多个元素,并通过一个共同的名称来引用这些元素。数组是一种很重要的数据结构,可以用于解决很多实际的问题。 在C语言中,定义数组的语法如下
    的头像 发表于 11-24 10:11 1153次阅读

    C语言如何创建数组

    C语言是一种非常强大和灵活的编程语言,它提供了若干数据类型来存储和操作数据。其中之一就是数组,它可以用来存储一系列具有相同数据类型的元素。本文将详细介绍如何在C语言中创建数组,并探讨数组的一些常见
    的头像 发表于 11-24 10:08 887次阅读

    将一维数组转为二维python

    将一维数组转为二维数组是一个常见的问题,特别是在处理数据时。一维数组是由一个连续的数据块组成,而二维数组则是由多个一维数组组成的
    的头像 发表于 11-23 14:54 2361次阅读

    什么是数组数组有什么用?数组的使用方法

    数组(Array)是有序的元素序列。
    的头像 发表于 11-08 14:58 745次阅读
    什么是<b class='flag-5'>数组</b>?<b class='flag-5'>数组</b>有什么用?<b class='flag-5'>数组</b>的使用方法

    Shell脚本程序开发-数组(四)

    数组中可以存放多个值。Bash Shell 只支持一维数组(不支持多维数组),初始化时不需要定义数组大小(与 PHP 类似)。
    的头像 发表于 10-22 17:50 298次阅读
    Shell脚本程序开发-<b class='flag-5'>数组</b>(四)

    数组的定义 什么是数组

    数组 数组是内置类型,是一组同类型数据的集合,它是值类型,通过从0开始的下标索引访问元素值。 在初始化后长度是固定的,无法修改其长度。当作为方法的参数传入时将复制一份数组而不是引用同一指针。
    的头像 发表于 10-09 09:39 1388次阅读

    C++数组名和数组拷贝详解

    C++数组间赋值不能直接通过数组名称 randy = sesame进行,因为数组名并不是指针,大部分情况下,编译器会隐式转换为指向数组首元素的指针常量。
    发表于 08-21 15:09 312次阅读
    C++<b class='flag-5'>数组</b>名和<b class='flag-5'>数组</b>拷贝详解

    C语言中指针数组数组指针的区别

    指针和数组之间存在着紧密的关系。在本文中,我们将探讨指针和数组的关系、指针算术和数组遍历、多维数组与指针以及指针数组
    发表于 08-17 15:29 288次阅读

    C 语言数组的基本结构

    数组是最基本的数据结构,关于数组的面试题也屡见不鲜,本文罗列了一些常见的面试题,仅供参考。目前有以下18道题目。 数组求和 求数组的最大值和最小值 求
    的头像 发表于 06-22 10:56 400次阅读

    定义了一个64K字节的数组,如果把数组定义成变量数组运行不了怎么解决?

    我定义了一个64K字节的数组,如果把数组定义成Const常量,编辑之后运行都正常。 但如果把数组定义成变量数组,可以编译(占的空间比较大:P
    发表于 06-13 08:36