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

算法专题四: 前缀和

目录

  • 1. 前缀和
  • 2. 二维前缀和
  • 3. 寻找数组的中心下标
  • 4. 除自身以外数组的乘积
  • 5. 和为k的子数组
  • 6. 和可被K整除的子数组
  • 7. 连续数组
  • 8. 矩阵区域和

博客主页:酷酷学!!!
感谢关注~


1. 前缀和

在这里插入图片描述

算法思路:

根据题意, 创建一个前缀和数组, dp[i] = dp[i -1] + arr[i], 再使用前缀和数组, 要求的区域ret = dp[r] - dp[l-1], 这里我们为什么要这样求dp[i]呢? 还要绕一大圈子, 直接相加不就行了 , 但是如果直接相加求还不如我们的暴力解法呢, 这里还要开辟空间, 但是我们使用dp[i]求解只需遍历一遍数组即可求出前缀和

需要注意:

  1. 创建前缀和数组默认从0开始, 如果我们从1开始访问则需要先将数组初始化好,
  2. 因为dp[i] 的大小可能超过int所以需要创建long long类型的数组.

编写代码:

#include <iostream>
#include <vector>
using namespace std;int main() 
{int n = 0, q = 0;cin>>n>>q;vector<long long> dp(n+1,0);int num = 0;for(int i = 1; i <= n; i++){cin>>num;dp[i] = dp[i-1] + num;}while(q--){int l,r;cin>>l>>r;cout<<dp[r] - dp[l-1]<<endl;}return 0;
}
// 64 位输出请用 printf("%lld")

2. 二维前缀和

在这里插入图片描述
算法思路:

在这里插入图片描述

在这里插入图片描述

分析题意, 很显然如果我们使用暴力解法一定会超时的.
首先分析题目, 我们可以先预处理出来一个前缀和矩阵如下图所示, 求出每一个dp[i][j]
在这里插入图片描述
然后我们使用dp[i][j],根据所求的区域我们就可以找出一个求出结果的公式, 于是我们就可以搞出来一个时间复杂度为O(N)的算法,当然空间复杂度也为O(N).

在这里插入图片描述
编写代码:

#include<iostream>
#include<vector>
using namespace std;
int main()
{   int n,m,q;cin>>n>>m>>q;vector<vector<int>> arr(n+1,vector<int>(m+1));for(int i = 1; i <= n; i++){for(int j = 1; j <= m; j++){cin>>arr[i][j];}}vector<vector<long long>> dp(n+1,vector<long long>(m+1));for(int i = 1; i <= n; i++){for(int j = 1; j <= m; j++){dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + arr[i][j];}}int x1,y1,x2,y2;while(q--){cin>>x1>>y1>>x2>>y2;cout<<dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1-1] + dp[x1-1][y1-1]<<endl;}return 0;
}

3. 寻找数组的中心下标

在这里插入图片描述

算法思路1:

分析题意, 发现可以使用前缀和来解决, 首先预处理出来一个前缀和数组, 然后我们只需在遍历一遍前缀和数组, 只要找到一个位置前面的区域和后面的区域相同, 则就找到了该位置, 但是注意我们的dp是从1开始的所以返回结果需要-1, 如果没有找到返回-1即可, 注意预处理前缀和数组的时候与原数组的映射关系.

编写代码:

class Solution {
public:int pivotIndex(vector<int>& nums) {int n = nums.size();vector<int> dp(n+1,0);for(int i = 1; i <= n ;i++){dp[i] = dp[i-1] + nums[i-1];}for(int i = 1; i <= n ;i++){if(dp[i-1] == (dp[n] - dp[i]))return i-1;}return -1;}
};

算法思路二:

既然我要算左边和右边的和是否相等, 那么我们不妨弄两个的dp数组, 一个前缀和一个后缀和, 对于前缀和我们只需要求出i之前的所有元素之和即可, 对于后缀和我们只需要求出i位置之后的所有元素之和即可, 但是注意细节我们需要考虑第一次计算会访问越界的情况, 所以我们需要提前把f[0]和g[n-1]这个位置处理好,而vector默认就为0.
在这里插入图片描述

在这里插入图片描述

编写代码:

class Solution {
public:int pivotIndex(vector<int>& nums) {int n = nums.size();vector<int> f(n),g(n);for(int i = 1 ; i < n; i++){f[i] = f[i-1] + nums[i-1];}for(int i = n - 2; i >= 0; i--){g[i] = g[i+1] + nums[i+1];}for(int i = 0; i < n ; i++){if(g[i] == f[i]) return i;}return -1;}
};

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

在这里插入图片描述
算法思路:

有了上一题思路二的方法, 对于这道题我们不难解决, 只需求出对于i之前的前缀积以及对于i之后的后缀积即可, 遍历i位置此时answer[i]的位置就是f[i]*g[i], 但是注意细节,对于f[0]和g[n-1]我们要处理成1, 默认第一个位置之前的积为1,最后一个位置也是.

在这里插入图片描述

编写代码:

class Solution {
public:vector<int> productExceptSelf(vector<int>& nums) {int n = nums.size();vector<int> f(n),g(n),ret(n);f[0] = g[n-1] = 1;for(int i = 1 ; i < n; i++)f[i] = f[i-1] * nums[i-1];for(int i = n-2; i >= 0; i--)g[i] = g[i+1] * nums[i+1];for(int i = 0; i < n;i++)ret[i] = g[i] * f[i];return ret;}
};

5. 和为k的子数组

在这里插入图片描述

算法思路:

首先我们肯定会想到暴力求解, 但是时间复杂度为O(N^2), 那可不可以使用双指针呢, 也就是我们的滑动窗口, 也不可以, 因为会有负数, 并没有单调性.

在这里插入图片描述

对于一个数组, 我们要求这个数组中和为k的子数组, 比如遍历到i位置, 我们要求i往前的和为k的子数组, 仅需从0位置查找, 找到和为sum[i] - k的子数组即可, 所以我们的前缀和思想又可以派上用场了.

首先我们先计算出前缀和, 但是我们并不需要创建一个前缀和数组, 仅需把每一次的结果记录到sum即可, 遍历一遍数组, 遍历到i位置就找i位置之前有没有sum = nums[i] - k的, 我们可以把每一次的结果放到哈希表中, 但是注意我们并不需要i这个位置的和, 而是i之前的, 所以我们只保存i之前的前缀和即可, 如果有则ret++,继续下一次查找, 但是注意如果整个数组等于k,也就是遍历到第一个位置我们需要在0到-1这个区间查找, 所以我们需要处理hash[0] = 1.

在这里插入图片描述

编写代码:

class Solution {
public:int subarraySum(vector<int>& nums, int k) {unordered_map<int,int> hash;int sum = 0, ret = 0;hash[0] = 1;for(int i = 0; i < nums.size();i++){sum += nums[i];if(hash.count(sum - k)) ret += hash[sum - k]; hash[sum]++;}return ret;}
};

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

在这里插入图片描述

算法思路:

本道题依然不能使用滑动窗口来解决, 更上一道题思路类似, 只不过我们这道题哈希表里面存放的是sum的余数, 当遍历到i位置的时候只需要判断在i位置之前和除以k的余数是否等于sum除以k的余数, 因为同余定理, 可以看下图

在这里插入图片描述
也就是我们仅需在i位置前找到x%k等于sum%即可, 所以判断的时候我们需要判断sum的余数, 所以哈希表里面我们需要存放余数, 但是C++中余数是由负数的, 负数和正数取余结果不一样, 所以我们需要进行修正. 使用sum遍历数组, 并且求出sum的余数r, 然后先判断sum之前的余数是否等于r, 如果等于则更新ret,最后将这个r也丢入到哈希表中.

在这里插入图片描述

在这里插入图片描述

编写代码:

class Solution {
public:int subarraysDivByK(vector<int>& nums, int k) {unordered_map<int,int> hash;hash[0 % k] = 1;int sum = 0, ret = 0;for(int i = 0; i < nums.size() ; i++){sum += nums[i];int r = (sum % k + k) % k;if(hash.count(r)) ret += hash[r];hash[r]++;}return ret;}
};

7. 连续数组

在这里插入图片描述

算法原理:

这一道题没有单调性, 我们还是不能使用滑动窗口, 先把0修改成-1,我们就可以发现规律, i位置和在[0,i-1]位置的值相等, 即就能找到k数组, 因为正负抵消, 遍历到i位置我们判断该位置之前是否有和正好等于sum, 如果有则我们更新结果, 这里注意我们哈希表中需要存储下标, 因为结果需要我们求出长度, 如果有重复的sum, 我们只保存最早出现的那个下标.
在这里插入图片描述

class Solution {
public:int findMaxLength(vector<int>& nums) {unordered_map<int,int> hash;hash[0] = -1;int sum = 0 ,ret = 0;for(int i = 0; i < nums.size(); i++){sum += nums[i] == 0 ? -1 : 1;if(hash.count(sum)) ret = max(ret,i - hash[sum]);else hash[sum] = i;}return ret;}
};

8. 矩阵区域和

在这里插入图片描述

算法思路:

首先读懂题意, 如下图所示, 也就是answer数组中的每一个位置就是mat当前位置的值向周围拓展k个单位元素的和.接下来进行分析

在这里插入图片描述

这道题要求我们求出拓展k个单位的和, 我们应该联想到一个算法, 二维前缀和dp.回忆一下二维前缀和的用法.如果说我们要求一个矩阵的前缀和数组,
则dp[ i ] [ j ] = dp[ i - 1 ] [ j ] + dp[ i ] [ j-1 ] - dp[ i - 1] [j - 1] + arr[ i ] [ j ]

在这里插入图片描述
对于使用则有, 如果我们要求某一段区域的和,
ret = dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1-1] + dp[x1-1][y1-1]

在这里插入图片描述
可以看出, 我们只要想求某段区域的和则前缀和数组中, 仅需知道这块区域的左上角下标个右下角下标即可,本题我们也可以用这样的思路, 但是有几个细节需要注意:

1, 注意我们创建dp数组时, 为了避免越界访问一般下标从1开始, 但是本题所给的数组mat是从下标0 开始, 所以我们它的dp数组需要稍作修改, 当时用到mat时, 将下标+1处理

在这里插入图片描述

然后创建我们的ret数组, ret数组中的每一个位置我们都需要使用到dp数组, 所以需要求每一个位置的左上角位置与右下角位置的下标, 以便于正确的使用dp数组.根据下面我们求出该区域与使用dp数组的映射关系, 然后对于每一个位置分别求其结果.

在这里插入图片描述
编写代码:

class Solution {
public:vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {int m = mat.size(), n = mat[0].size();vector<vector<int>> dp(m + 1,vector<int>(n + 1));for(int i = 1; i <= m; i++){for(int j = 1; j <= n; j++){dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + mat[i-1][j-1];}}vector<vector<int>> ret(m,vector<int>(n));for(int i = 0; i< m;i++){for(int j = 0;j < n; j++){int x1 = max(0,i-k) + 1, y1 = max(0,j-k) + 1;int x2 = min(m-1,i+k) + 1, y2 = min(n-1,j+k) + 1;ret[i][j] = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1-1] +dp[x1-1][y1-1];}}return ret;}
};

相关文章:

算法专题四: 前缀和

目录 1. 前缀和2. 二维前缀和3. 寻找数组的中心下标4. 除自身以外数组的乘积5. 和为k的子数组6. 和可被K整除的子数组7. 连续数组8. 矩阵区域和 博客主页:酷酷学!!! 感谢关注~ 1. 前缀和 算法思路: 根据题意, 创建一个前缀和数组, dp[i] dp[i -1] arr[i], 再使用前缀和数组,…...

【Linux】基础IO(文件描述符、缓冲区、重定向)

&#x1f308;个人主页&#xff1a;秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343&#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/qinjh_/category_12625432.html 目录 前言 C文件IO相关操作 系统文件I/O open open函数返回值 文件描述符fd re…...

一篇文章快速学会docker容器技术

目录 一、Docker简介及部署方法 1.1Docker简介 1.1.1什么是docker 1.1.2 docker在企业中的应用场景 1.1.3 docker与虚拟化的对比 1.1.4 docker的优势 二 、部署docker 2.1 容器工作方法 2.2 部署第一个容器 2.2.1 配置软件仓库 2.2.2 安装docker-ce并启动服务 2.2.…...

【MySQL】使用 JDBC 连接数据库

文章目录 前言1. 认识 JDBC1.1 概念1.2 好处 2. 使用 JDBC2.1 安装数据驱动包2.2 把 jar 包导入到项目中2.3 代码编写2.4 测试结果 3. 代码优化4. 源码展示结语 前言 在 MySQL 系列中&#xff0c;我们介绍了很多内容&#xff0c;包括但不限于建库建表&#xff0c;增删查改等等…...

数据结构与算法笔记:概念与leetcode练习题

1、数组Array 时间复杂度 数组访问&#xff1a;O(1) 数组搜索&#xff1a;O(N) 数组插入&#xff1a;O(N) 数组删除&#xff1a;O(N) 特点 适合读&#xff0c;不适合写 数组常用操作 # 1、创建数组 a [] # 2、尾部添加元素 a.append(1) a.append(2) a.append(3) # 3、…...

十大时间序列预测模型

目录 1. 自回归模型 原理 核心公式 推导过程: 完整案例 2. 移动平均模型 原理 核心公式 推导过程: 完整案例 3. 自回归移动平均模型 原理 核心公式 推导过程: 完整案例 4. 自回归积分移动平均模型 原理 核心公式 推导过程 完整案例 5. 季节性自回归积分…...

G2O 通过工厂函数类 OptimizationAlgorithmFactory 来生成固定搭配的优化算法

OptimizationAlgorithmFactory 类位于 optimization_algorithm_factory.h //***g2o源码 g2o/g2o/core/optimization_algorithm_factory.h ***// /*** \brief create solvers based on their short name** Factory to allocate solvers based on their short name.* The Factor…...

手机USB连接不显示内部设备,设备管理器显示“MTP”感叹号,解决方案

进入小米驱动下载界面&#xff0c;等小米驱动下载完成后&#xff0c;解压此驱动文件压缩包。 5、小米USB驱动安装方法&#xff1a;右击“计算机”&#xff0c;从弹出的右键菜单中选择“管理”项进入。 6、在打开的“计算机管理”界面中&#xff0c;展开“设备管理器”项&…...

SpringBootWeb快速入门!详解如何创建一个简单的SpringBoot项目?

在现代Web开发中&#xff0c;SpringBoot以其简化的配置和快速的开发效率而受到广大开发者的青睐。本篇文章将带领你从零开始&#xff0c;搭建一个基于SpringBoot的简单Web应用~ 一、前提准备 想要创建一个SpringBoot项目&#xff0c;需要做如下准备&#xff1a; idea集成开发…...

RabbitMQ 入门到精通指南

RabbitMQ 是一种开源消息代理软件&#xff0c;基于 AMQP&#xff08;高级消息队列协议&#xff09;构建&#xff0c;用于异步传输数据&#xff0c;帮助我们解耦系统、削峰流量、处理高并发。本指南将详细介绍 RabbitMQ 的架构设计、使用场景、安装步骤以及一些高级应用&#xf…...

ARM base instruction -- movz

Move wide with zero moves an optionally-shifted 16-bit immediate value to a register. 用零移动宽值将可选移位的16位即时值移动到寄存器。即把立即数移动寄存器前先把寄存器清零。 32-bit variant MOVZ <Wd>, #<imm>{, LSL #<shift>} 64-bit var…...

安装jdk安装开发环境与maven

1.下载maven 链接: https://pan.baidu.com/s/1gTmIWBFBdIQob0cqGG3E_Q 提取码: 42ck&#xff0c;apache-maven-3.8.4-bin.zip 2.安装java jdk yum install -y java-1.8.0-openjdk-devel 3.在/opt目录下新建目录 mkdir /opt/maven 4.将apache-maven-3.8.4-bin.zip上传到/opt/ma…...

openpnp - 图像传送方向要在高级校正之前设置好

文章目录 openpnp - 图像传送方向要在高级校正之前设置好笔记图像传送方向的确定END openpnp - 图像传送方向要在高级校正之前设置好 笔记 图像传送方向和JOG面板的移动控制和实际设备的顶部摄像头/底部摄像头要一致&#xff0c;这样才能和贴板子时的实际操作方向对应起来。 …...

数据库建表规范【记录】

建表规约 【强制】创建表时必须显式指定表存储引擎类型&#xff0c;如无特殊需求&#xff0c;一律为InnoDB。 【强制】必须有行数据的创建时间字段create_date和最后更新时间字段edit_date。 【强制】自增主键命名必须是id&#xff0c;关联表外键命名xxyyzz_id&#xff1b;业务…...

css的动画属性

CSS动画属性是CSS3的一个重要特性&#xff0c;它允许你创建平滑的过渡效果&#xff0c;增强用户的交互体验。CSS动画可以通过keyframes规则和animation属性来创建。 animation属性 animation属性是一个简写属性&#xff0c;用于设置动画的多个属性&#xff0c;包括动画名称、…...

【Ubuntu】PlantUML工具 | 安装 | 语法 | 使用工具画序列图

🌱 PlantUML是一个通用性很强的工具,可以快速、直接地创建各种图表。 目录 1 安装 2 使用PlantUML画序列图 ① 语法 ②示例和效果 利用简单直观的语言,用户可以毫不费力地绘制各种类型的图表。PlantUML 是一个开源项目,支持快速绘制:• 时序图• 用例图• 类图• 对...

微信步数C++

题目&#xff1a; 样例解释&#xff1a; 【样例 #1 解释】 从 (1,1) 出发将走 2 步&#xff0c;从 (1,2) 出发将走 4 步&#xff0c;从 (1,3) 出发将走 4 步。 从 (2,1) 出发将走 2 步&#xff0c;从 (2,2) 出发将走 3 步&#xff0c;从 (2,3) 出发将走 3 步。 从 (3,1) 出发将…...

AI写作工具大比拼:揭秘Claude的神秘魅力以及如何订阅Claude

AI写作困境与Claude的惊喜表现 最近有很多朋友在吐槽AI写的文章不太行&#xff0c;我一看他的要求写的很清楚&#xff0c;已经把提示词都用到位了&#xff0c;例如&#xff1a;写作背景、写作要求等&#xff0c;都有具体写出来。但文章阅读起来就是欠缺点啥。 你们有没有遇到…...

秋招内推2025-招联金融

【投递方式】 直接扫下方二维码&#xff0c;或点击内推官网https://wecruit.hotjob.cn/SU61025e262f9d247b98e0a2c2/mc/position/campus&#xff0c;使用内推码 igcefb 投递&#xff09; 【招聘岗位】 后台开发 前端开发 数据开发 数据运营 算法开发 技术运维 软件测试 产品策…...

GOM引擎启动后M2提示Invalid filename报错的解决办法

在架设一个GOM引擎版本的时候&#xff0c;启动M2就提示Invalid filename&#xff0c;之后的网关就没有办法再启动了&#xff0c;研究了半天也终于是弄好了&#xff0c;其实也简单&#xff0c;就是路径设置的不对&#xff0c;所以无法完成启动&#xff0c;很多人以为在控制台设置…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

Element Plus 表单(el-form)中关于正整数输入的校验规则

目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入&#xff08;联动&#xff09;2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...

Bean 作用域有哪些?如何答出技术深度?

导语&#xff1a; Spring 面试绕不开 Bean 的作用域问题&#xff0c;这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开&#xff0c;结合典型面试题及实战场景&#xff0c;帮你厘清重点&#xff0c;打破模板式回答&#xff0c…...

Chrome 浏览器前端与客户端双向通信实战

Chrome 前端&#xff08;即页面 JS / Web UI&#xff09;与客户端&#xff08;C 后端&#xff09;的交互机制&#xff0c;是 Chromium 架构中非常核心的一环。下面我将按常见场景&#xff0c;从通道、流程、技术栈几个角度做一套完整的分析&#xff0c;特别适合你这种在分析和改…...

通过MicroSip配置自己的freeswitch服务器进行调试记录

之前用docker安装的freeswitch的&#xff0c;启动是正常的&#xff0c; 但用下面的Microsip连接不上 主要原因有可能一下几个 1、通过下面命令可以看 [rootlocalhost default]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"Name …...