当前位置: 首页 > news >正文

LeetCode-数组-前缀和-中等难度

前缀和

前缀和是一种利用预处理的方式来减少整体实现复杂度的方法。

基本定理

假设原数列A为:[1,2,3,4,5],与之对应的前缀和数列P则为:[1,3,6,10,15]

前缀和数列的第一项等于原数列的第一项,从第二项开始前缀和数列每一项计算方法为:P[i] = P[i-1]+A[i]

原数列A与前缀和数列P则有以下几种关系:

  1. 前缀和数列从第二项起,每一项(i)与它的前一项(i - 1)的差等于原数列(i)的值。
  2. 前缀和数列每一项(i)等于原数列中(0 ~ i)项之和。
  3. 在满足0 < i < j时,原数列的第(i ~ j)项之和等于前缀和数列的第(j)项减去第(i - 1)项。

根据这样的关系,在某些应用场景上我们就可以利用前缀和进行快速求解。

1. 区域和检索 - 数组不可变

题目描述

在这里插入图片描述

解题思路

根据前面定理中的第三条,可以发现利用前缀和可以快速实现这个需求。

代码实现

这里为了方便计算,额外忽视了前缀和数列的第一位,比如数列为:{1,2,3,4,5},对应的前缀和数列为:{0,1,3,6,10,15},其中第一位数值0,没有任何含义,只是为了方便处理。

class NumArray {private int[] preSum;public NumArray(int[] nums) {preSum = new int[nums.length + 1];for(int i = 0; i < nums.length; i++){preSum[i + 1] = preSum[i] + nums[i];}}public int sumRange(int left, int right) {// 本身根据定理应该是p[right] - p[left - 1],// 但由于我们将前缀和数列整体右移了一位,因此就变成了p[right + 1] - p[left],// 这样做的好处是,不用单独处理当left为0的情况了。return preSum[right + 1] - preSum[left];}
}

2. 除自身以外数组的乘积

题目描述

在这里插入图片描述

解题思路

本题可以考虑从后向前遍历,那么每一项之前的元素乘积,如果用前缀和思路来处理的话,就是前面定理中的第二条。(符合前缀和的也符合前缀乘积)

比如原数列:{1,2,3,4},前缀积数列:{1,2,6,24},则遍历原数列最后一位4时,对应的之前所有数的乘积就是前缀积数列的倒数第二位6

现在已经搞定了除目标元素外的左边所有元素的乘积,只要再计算出其右边所有元素的乘积即可。

依据同样的思想,你也可以做一个后缀积来方便直接获取,但就当前这个场景来说倒也没必要,对于右边元素我们只需要直接动态维护即可。

代码实现

class Solution {public int[] productExceptSelf(int[] nums) {int n = nums.length;int[] preSum = new int[n + 1];// 前缀数列第一位无实际意思,仅仅为了方便处理preSum[0] = 1;for (int i = 1; i < n + 1; i++) {preSum[i] = preSum[i - 1] * nums[i - 1];}// 右边元素动态维护int rightSum = 1;int[] ans = new int[n];for (int i = n - 1; i >= 0; i--) {ans[i] = preSum[i] * rightSum;rightSum *= nums[i];}return ans;}
}

3. 和相同的二元子数组

题目描述

在这里插入图片描述

解题思路

假设有:原数列1 0 1 0 1 ,和与其对应的前缀和数列:1 1 2 2 3

  1. 我们知道子数组可以看作[left,right]这样一个区间,并且leftright满足:0 <= left <= right.
  2. 所以本题可以看作是在要求0 <= left <= right这样一个区间内的所有元素和等于goal
  3. 我们看到求解0 <= left <= right这样一个区间之和,就可以思考一下是否可以利用前缀和的性质。很明显在前缀和数列中只需要通过计算right - (left - 1),即可得到原数列的0 <= left <= right区间之和。
  4. 经过上面3步分析,最终就变成了求right - (left - 1)等于goal的个数。

所以最终我们只需要构造一个前缀和数列,然后通过嵌套循环的方式,以每个right为子数组的结束区间,挨个检查子数组的开始区间即可。

public int numSubarraysWithSum(int[] nums, int goal) {int[] perSum = new int[nums.length + 1];for (int i = 0; i < nums.length; i++) {perSum[i + 1] = perSum[i] + nums[i];}int ans = 0;for (int right = 1; right < perSum.length; right++) {for (int left = right; left > 0; left--) {if (perSum[right] - perSum[left - 1] == goal) {ans++;}}}return ans;
}

复杂度优化

由于存在嵌套循环,因此复杂度较高,不难发现,每次内层循环中只是在计算在0~right-1下标对应的数值中有多少个等于perSum[right] - goal,因此我们考虑将每一个0~right-1对应的结果直接记录下来,这样不就可以省去内层循环的处理了吗~

public int numSubarraysWithSum(int[] nums, int goal) {int[] perSum = new int[nums.length + 1];for (int i = 0; i < nums.length; i++) {perSum[i + 1] = perSum[i] + nums[i];}int ans = 0;// key表示前缀和数列坐标对应的值,value表示改值出现的次数Map<Integer, Integer> map = new HashMap<>();// 前缀和第一位坐标0需特殊处理一下,对应的值为0,出现1次。map.put(0, 1);for (int right = 1; right < perSum.length; right++) {if (map.containsKey(perSum[right] - goal)) {ans += map.get(perSum[right] - goal);}map.put(perSum[right], map.getOrDefault(perSum[right], 0) + 1);}return ans;
}

如果还想简化一点,则可以动态维护前缀和数组,如下:

public int numSubarraysWithSum(int[] nums, int goal) {int ans = 0;Map<Integer, Integer> map = new HashMap<>();map.put(0, 1);int perSum = 0;for (int right = 0; right < nums.length; right++) {perSum += nums[right];if (map.containsKey(perSum - goal)) {ans += map.get(perSum - goal);}map.put(perSum, map.getOrDefault(perSum, 0) + 1);}return ans;
}

类似题型

与之类似的题目还有:

560.和为K的子数组

974.和可被 K 整除的子数组

4. 连续的子数组和

题目描述

在这里插入图片描述

解题思路

本题关键在于解决子数组元素总和为K的倍数这个问题。

首先,我们发现是求解子数组的问题,通过前面的练习,我们知道在前缀和数列中,求解子数组的和,等同于求解其前缀和数组的right - (left - 1)

同余定理

给定一个正整数m,如果两个整数a和b满足a-b能够被m整除,即(a-b)/m得到一个整数,那么就称整数a与b对模m同余,记作a≡b(mod m)。

因此,如果(right - (left - 1))是K的整数倍,则根据同余定理则有 right % k == (left - 1) % k

有了这个结论以后,接下来的处理方式就和前面差不多了,通过一个哈希表用Key记录每一项的余数,Value用来记录下标,为了满足题目中大小至少为2的要求,下标位置不更新。

代码实现

public boolean checkSubarraySum(int[] nums, int k) {int sum = 0;Map<Integer, Integer> map = new HashMap<>();map.put(0, -1);for (int i = 0; i < nums.length; i++) {sum += nums[i];int mod = sum % k;if (map.containsKey(mod)) {if (i - map.get(mod) >= 2) {return true;}} else {map.put(mod, i);}}return false;
}

5. 连续数组

题目描述

在这里插入图片描述

解题思路

如果把拥有相同数量的01理解为互相抵消,假设我们有一个计数器C,当遇到0就减1,遇到1就加1,因此当C等于0时,则01的数量一定相同,所以这个题目就又变成了子数组求和的问题了,那么看到子数组求和就想到用前缀和数列来解。

我们知道子数组可以看作[left,right],其所有元素之和,又等于其对应的前缀和数列right - (left - 1)的计算结果,这个在前面几题中也一直在用,又因为如果right == (left - 1),则right - (left - 1)==0,也就是我们要求的结果,子数组区间内所有元素之和为0。所以,最后我们要求的其实就是长度最长的right == (left - 1)

代码实现

public int findMaxLength(int[] nums) {Map<Integer, Integer> map = new HashMap<>();// 表示0值,出现的下标位置map.put(0, -1);int sum = 0;int ans = 0;for (int i = 0; i < nums.length; i++) {sum += nums[i] == 0 ? -1 : 1;if (map.containsKey(sum)) {ans = Math.max(ans, i - map.get(sum));} else {map.put(sum, i);}}return ans;
}

类似题型

与之类似的题目还有:

字母与数字

6. 和为奇数的子数组数目

题目描述

在这里插入图片描述

解题思路

还是求子数组和的问题,要求为奇数,我们知道,两奇数或两偶数相加结果为偶数,只有一奇一偶相加结果才为奇数。

所以,根据right - (left - 1)等于原数组的子数组之和,可以得出以下结论:

  1. right为偶数时,left - 1必须为奇数,子数组之和才为奇数。

  2. right为奇数时,left - 1必须为偶数,子数组之和才为奇数。

代码实现

public int numOfSubarrays(int[] arr) {int ans = 0;int mod = 1000000007;int odd = 0;int even = 1;int sum = 0;for (int i = 0; i < arr.length; i++) {sum += arr[i];ans = (ans + (sum % 2 == 0 ? odd : even)) % mod;if(sum % 2 == 0){even++;}else{odd++;}}return ans;
}

7. 统计「优美子数组」

题目描述

在这里插入图片描述

解题思路

可以把奇数的个数当做前缀和数列来记录,比如对于[1,1,2,1,1]这样的数列,对应的奇数前缀和数列为[0,1,2,2,3,4],每一项表示的是奇数个数(注意:0个奇数也是有计算含义的)。

那么如果子数组恰好有K个奇数,则有right - (left - 1) = K,转换一下也就是right - K = left - 1,这就回到了我们习惯的哈希表处理方式,无需说明,直接看代码。

代码实现

public int numberOfSubarrays(int[] nums, int k) {int ans = 0;int oddCnt = 0;Map<Integer, Integer> map = new HashMap<>();// 初始化map表示:出现0个奇数的次数为1map.put(0, 1);for (int i = 0; i < nums.length; i++) {if (nums[i] % 2 != 0) {oddCnt++;}if (map.containsKey(oddCnt - k)) {ans += map.get(oddCnt - k);}map.put(oddCnt, map.getOrDefault(oddCnt, 0) + 1);}return ans;
}

相关文章:

LeetCode-数组-前缀和-中等难度

前缀和 前缀和是一种利用预处理的方式来减少整体实现复杂度的方法。 基本定理 假设原数列A为&#xff1a;[1,2,3,4,5]&#xff0c;与之对应的前缀和数列P则为&#xff1a;[1,3,6,10,15] 前缀和数列的第一项等于原数列的第一项&#xff0c;从第二项开始前缀和数列每一项计算…...

【程序人生】探索2024年AI辅助研发趋势

目录标题 探索2024年AI辅助研发趋势一、AI在编码中的应用智能代码生成助力开发错误检测与修复的即时反馈性能优化的智能建议 二、AI驱动的自动化工具三、AI与团队协作四、未来展望结语 探索2024年AI辅助研发趋势 随着人工智能技术的迅速发展&#xff0c;AI在各个领域的应用正日…...

集合框架(一)Collection

学习过了ArrayList&#xff0c;知道集合是一种容器&#xff0c;用来装数据的&#xff0c;类似于数组&#xff0c;但集合的大小可变&#xff0c;开发中也非常常用。 为了满足不同的业务场景需求Java还提供了很多不同特点的集合给我们选择。 集合体系结构 Collection是一个接口&a…...

Android 性能优化--APK加固(2)加密

文章目录 字符串加密图片加密如何避免应用被重新签名分发APK 加壳的方案简析DEX加密原理及实现 本文首发地址&#xff1a;https://h89.cn/archives/212.html 最新更新地址&#xff1a;https://gitee.com/chenjim/chenjimblog 通过 前文 介绍&#xff0c;我们知晓了如何使用代码…...

Linux环境下使用interrupt方式操作UART

目录 概述 1 Linux环境下UART设备 2 轮询方式操作UART功能实现 2.1 打开串口函数&#xff1a;usr_serial_open 2.2 关闭串口函数&#xff1a; usr_serial_close 2.3 发送数据函数&#xff1a; usr_serial_sendbytes 2.4 接收数据函数&#xff1a; usr_serial_readinterr…...

修改Android打包apk的名字和目录

app打包生成apk后通常需要进行备份&#xff0c;但是要区分好哪个apk是什么版本的、什么时候打包的&#xff0c;以方便以后区分使用。 最开始的想法是把版本号、创建时间这些加在apk文件名上即可&#xff0c;但是公司要求apk使用一个固定的名称&#xff0c;那我怎么保存版本号信…...

管理 PostgreSQL 中配置参数的各种方法

管理 PostgreSQL 中配置参数的各种方法 1. 概述 PostgreSQL提供了一个配置文件 postgresql.conf 让用户自定义参数。您可能需要更改一些参数来调整性能或在工作环境中部署 PostgreSQL 服务器。在这篇博文中&#xff0c;我们将探索管理这些参数的不同方法。 2. 以不同方式管理…...

Linux命令-continue命令(结束本次循环,继续执行下一个for,while或until循环。)

概要 continue [n]主要用途 结束本次循环&#xff0c;继续执行下一个for&#xff0c;while或until循环&#xff1b;可指定从第几层循环继续执行。 参数 n&#xff08;可选&#xff09;&#xff1a;大于等于1的整数&#xff0c;用于指定从第几层循环继续执行。 返回值 返回…...

智能部署之巅:Amazon SageMaker 引领机器学习革新

本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 亚马逊云科技开发者社区, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道。 &#xff08;全球 TMT 2023年12月6日讯&#xff09;亚马逊云科技在 2023 re:Invent 全…...

国内哪个工具可以平替chatgpt?国内有哪些比较好用的大模型gpt?

我自己试用了很多的平台&#xff0c;发现三个比较好的大模型平台&#xff0c;对普通用户也比较的友好的&#xff0c;而且返回内容相对来说&#xff0c;正确率更高的&#xff0c;并且相关场景插件比较丰富的国内厂商。 本文说的&#xff0c;是我自己觉得的&#xff0c;比较有主观…...

python如何打包py文件为exe

要将Python程序打包为可执行文件&#xff08;.exe&#xff09;&#xff0c;您可以使用一些第三方工具。以下是两个常用的工具&#xff1a;PyInstaller和cx_Freeze。 使用PyInstaller PyInstaller是一个流行的Python打包工具&#xff0c;可以将Python程序及其所有依赖项打包为…...

yolov9网络结构图

文章目录 配置文件主干分支backbone预测头headyolov9网络结构图 系列文章目录 论文链接&#xff1a;&#x1f47f; YOLOv9: Learning What You Want to Learn Using Programmable Gradient Information代码链接&#xff1a;&#x1f47f; https://github.com/WongKinYiu/yolov9…...

Spark 核心API

核心 API spark core API 指的是 spark 预定义好的算子。无论是 spark streaming 或者 Spark SQL 都是基于这些最基础的 API 构建起来的。理解这些核心 API 也是写出高效 Spark 代码的基础。 Transformation 转化类的算子是最多的&#xff0c;学会使用这些算子就应付多数的数…...

OpenLayers线性渐变和中心渐变(径向渐变)

目录 1.前言2.添加一个面要素3.线性渐变3.1 第一个注意点3.2 第二个注意点 4.中心渐变&#xff08;径向渐变&#xff09;5.总结 1.前言 OpenLayers官网有整个图层的渐变示例&#xff0c;但是没有单个要素的渐变示例&#xff0c;我们这里来补充一下。OpenLayers中的渐变是通过fi…...

[210. 课程表 II] 拓扑排序模板(DFS+BFS)

Problem: 210. 课程表 II 文章目录 思路解题方法Code 思路 本题是经典拓扑排序模板&#xff0c;通过DFS和BFS两种方式进行实现。 解题方法 DFS DFS方法的重点在于如何标记节点状态&#xff0c;初做题者如果只用未访问和已访问两种状态很容易陷入死结。正确的做法是使用三种状…...

我的第一个python web 网站

# -*- coding: utf-8 -*-import http.server import socketserver from datetime import datetimePORT 8000import sys# ...class MyHandler(http.server.SimpleHTTPRequestHandler):def do_GET(self):if self.path /:# 如果路径是根路径&#xff0c;返回页面内容self.send_r…...

产品展示型wordpress外贸网站模板

孕婴产品wordpress外贸网站模板 吸奶器、待产包、孕妇枕头、护理垫、纸尿裤、孕妇装、孕婴产品wordpress外贸网站模板。 https://www.jianzhanpress.com/?p4112 床品毛巾wordpress独立站模板 床单、被套、毛巾、抱枕、靠垫、围巾、布艺、枕头、乳胶枕、四件套、浴巾wordpre…...

四信全球化拓展再启新篇!LoRa传感器与云平台领航智能感知时代

随着科技浪潮的不断推进&#xff0c;物联网已逐渐融入我们的生活。刚刚结束的MWC24盛会上&#xff0c;四信带来了一系列前沿技术成果&#xff0c;不仅将5G技术成功扩展至当前市场主流类型的终端&#xff0c;更携手联通、ASR等业界巨头&#xff0c;在连接、5G RedCap、AI、LoRa以…...

阿里云k8s环境下,因slb限额导致的发布事故

一、背景 阿里云k8s容器&#xff0c;在发布java应用程序的时候&#xff0c;客户端访问出现500错误。 后端服务是健康且可用的&#xff0c;网关层大量500错误请求&#xff0c;slb没有流入和流出流量。 经过回滚&#xff0c;仍未能解决错误。可谓是一次血的教训&#xff0c;特…...

【STM32+OPENMV】矩形识别

一、准备工作 有关OPENMV最大色块追踪及与STM32通信内容&#xff0c;详情见【STM32HAL】与OpenMV通信 二、所用工具 1、芯片&#xff1a;STM32F103C8T6 2、CUBEMX配置软件 3、KEIL5 4、OPENMV 三、实现功能 寻找黑色矩形&#xff0c;并将最大矩形的四个边缘坐标发送给STM…...

在吗?腾讯云服务器优惠价格表曝光_2023年3月报价请过目!

腾讯云服务器多少钱一年&#xff1f;61元一年起&#xff0c;2核2G3M配置&#xff0c;腾讯云2核4G5M轻量应用服务器165元一年、756元3年&#xff0c;4核16G12M服务器32元1个月、312元一年&#xff0c;8核32G22M服务器115元1个月、345元3个月&#xff0c;腾讯云服务器网txyfwq.co…...

Revit-二开之创建Plane-(7)

2016版本的Plane 2017版本的Plane 2018版本及以上版本的Plane 由此可见2017版本是一个分水岭 #if REVIT2016Plane plane = new Plane(uiDoc.Document.ActiveView...

【操作系统学习笔记】文件管理1.2

【操作系统学习笔记】文件管理1.2 参考书籍: 王道考研 视频地址: Bilibili 文件的逻辑结构 无结构文件 文件内部的数据就是一系列的二进制流或字符流组成&#xff0c;又称流式文件&#xff0c;例如 .text 文件 有结构文件 由一组相似的记录组成&#xff0c;又称记录式文件…...

算法归纳【数组篇】

目录 二分查找1. 前提条件&#xff1a;2. 二分查找边界 2.移除元素有序数组的平方长度最小的子数组59.螺旋矩阵II54. 螺旋矩阵 二分查找 参考链接 https://programmercarl.com/0704.%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.html#%E6%80%9D%E8%B7%AF 1. 前提条件&#xff1a; 数…...

【随笔】程序员如何选择职业赛道,目前各个赛道的现状如何,那个赛道前景巨大

大家好&#xff0c;我是全栈小5&#xff0c;欢迎阅读文章&#xff01; 此篇是【话题达人】系列文章&#xff0c;这一次的话题是《程序员如何选择职业赛道》 目录 背景热度柱状图赛道热度C/C云原生人工智能前沿技术软件工程后端JavaJavascriptPHPPython区块链大数据移动开发嵌入…...

进程之舞:操作系统中的启动、状态转换与唤醒艺术

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua&#xff0c;在这里我会分享我的知识和经验。&#x…...

Java面试(4)之 Spring Bean生命周期过程

一, 整个加载的完整链路图 更详细的生命周期函数链路图(仅供参考) 二, Bean实例化的四种方式: 1, 无参构造器(默认且常用)6 2, 静态工厂方法方式(factory-method指定实例化的静态方法) 3, 实例工厂方法方式(factory-bean指定bean的name,factory-method指定实例化方法) 4, 实…...

JavaSE——面向对象高级一(1/4)-static修饰成员变量、应用场景,static修饰成员方法、应用场景

目录 static修饰成员变量 类变量的应用场景 static修饰成员方法 static修饰成员方法的应用场景 static 叫静态&#xff0c;可可以修饰成员变量、成员方法。 成员变量按照有无static修饰&#xff0c;分为两种&#xff1a; 类变量实例变量&#xff08;对象的变量&#xff…...

轻量脚本语言Lua的配置与c++调用

文章目录 lua配置下载运行lua命令lua脚本的执行C++调用lua环境配置错误和警告测试c++程序lua脚本结果Lua是一种功能强大且快速的编程语言,易于学习和使用,并且可以嵌入到应用程序中。 Lua被设计成一种轻量级的可嵌入脚本语言。它被用于各种各样的应用程序,从游戏到web应用程…...

力扣每日一道系列 --- LeetCode 160. 相交链表

&#x1f4f7; 江池俊&#xff1a; 个人主页 &#x1f525;个人专栏&#xff1a; ✅数据结构探索 ✅LeetCode每日一道 &#x1f305; 有航道的人&#xff0c;再渺小也不会迷途。 LeetCode 160. 相交链表 思路&#xff1a; 首先计算两个链表的长度&#xff0c;然后判断两个链…...