当前位置: 首页 > 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…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝

目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为&#xff1a;一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...

Linux系统部署KES

1、安装准备 1.版本说明V008R006C009B0014 V008&#xff1a;是version产品的大版本。 R006&#xff1a;是release产品特性版本。 C009&#xff1a;是通用版 B0014&#xff1a;是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存&#xff1a;1GB 以上 硬盘&#xf…...

如何应对敏捷转型中的团队阻力

应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中&#xff0c;明确沟通敏捷转型目的尤为关键&#xff0c;团队成员只有清晰理解转型背后的原因和利益&#xff0c;才能降低对变化的…...

深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏

一、引言 在深度学习中&#xff0c;我们训练出的神经网络往往非常庞大&#xff08;比如像 ResNet、YOLOv8、Vision Transformer&#xff09;&#xff0c;虽然精度很高&#xff0c;但“太重”了&#xff0c;运行起来很慢&#xff0c;占用内存大&#xff0c;不适合部署到手机、摄…...