C++算法 —— 前缀和

一、【模版】前缀和

1.链接

【模板】前缀和_牛客题霸_牛客网 (nowcoder.com)

2.描述

3.思路

前缀和的思想其实就是一种简单的动态规划,以i位置记录从头位置到i位置的和,然后间接的求一段连续区间的数组和,时间复杂度是O(n) + O(q),这种思想在实际中是为了应对多次查询的情况,当q特别大时,采用这种方式的时间复杂度就会较低

4.参考代码

#include <iostream>
#include <vector>
using namespace std;

int main() 
{
    int n,q;
    cin >> n >> q;
    vector<int> arr(n+1,0);
    for(int i = 1;i <= n;i++) cin >> arr[i];
    vector<long long> dp(n+1,0);
    for(int i = 1;i <= n ; i++) dp[i] = dp[i-1] + arr[i];

    while(q--)
    {
        int l,r;
        cin >> l >> r;
        cout << dp[r] - dp[l-1] << endl;
    }
    return 0;
}

二、二维前缀和

1.链接

【模板】二维前缀和_牛客题霸_牛客网 (nowcoder.com)

2.描述

3.思路

该题我们采用动态规划的定式思路去分析得到前缀和的表,并且分析如何使用

4.参考代码

核心的思路就是上面的动归分析已经如何使用前缀和表格的部分,剩下的就是在实际写代码时候的一些细节

1.测试用例中包含较大的数据,因此在dp表格中存放的值类型需要使用long long

2.一般按照思路是先载入数据,然后再动归建立dp表,但由于我们这里dp表和arr都选择使用多一行一列的辅助位置去进行的初始化,这两个步骤可以放在同一个for循环里一起执行,但先后顺序不能变,一定是先载入arr的数据,再去执行dp

#include <iostream>
using namespace std;
#include<vector>

int main() 
{
    //加载数据
    int n,m,q;
    cin >> n >> m >> q;
    vector<vector<int>> arr(n+1,vector<int>(m+1,0));
    vector<vector<long long>> dp(n+1,vector<long long>(m+1,0));
    //利用动态规划思路建立前缀和的表格
    //写代码的时候发现,两个步骤可以合并,因此放在一起执行
    for(int i = 1;i<=n;i++)
    {
        for(int j = 1;j<=m;j++)
        {
            cin >> arr[i][j];//加载数据
            dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + arr[i][j];//动归创建前缀和表格
        }
    }

    int x1,x2,y1,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;
}

三、寻找数组的中心下标

1.链接

724. 寻找数组的中心下标 - 力扣(LeetCode)

2.描述

3.思路

先建立前缀和表格,然后遍历一遍下标位置,去比对当前下标的前后两个部分的和是否相同,若是相同则说明当前下标就是目标值,直接返回,若是遍历结束后都没有找到,说明不存在,返回-1

要注意前缀表中和题目给的数组两者之间的映射关系,最好画图去分析,又或者可以建立多一个后缀表,去对应遍历

4.参考代码

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 = 0;i<n;i++)
        {
            if(dp[i] == dp[n] - dp[i+1]) return i;
        }
        return -1;
    }
};

四、除自身以外数组的乘积

1.链接

238. 除自身以外数组的乘积 - 力扣(LeetCode)

2.描述

3.思路

题目要求的数组是除开自己的其余所有数的乘积,那么可以将ret[i]分成两部分

1.nums[0] * nums[1] * nums[2] * ... * nums[i-1] (i位置的左半部分乘积)

2.nums[i+1] * nums[i+2] * nums[i+3] * ... *  nums[n-1](i位置的右半部分乘积)

因此我们可以利用前缀和的思想,去将前半部分和后半部分分别进行制表

head[i]:表示以i位置结束,从第头到该位置的乘积

tail[i]:表示从i位置开始,到最末尾的乘积

然后遍历填表即可

4.参考代码

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) 
    {
        int n = nums.size();
        vector<int> ret(n);
        vector<int> head(n+1,1);//添加辅助位要注意映射关系
        vector<int> tail(n+2,1);
        //注意,这里需要先初始化tail的第一个值
        for(int i = 1;i<=n;i++) head[i] = head[i-1]*nums[i-1];
        for(int i = n;i>=1;i--) tail[i] = tail[i+1]*nums[i-1];
        //得到两个表格后,遍历填表即可
        for(int i = 0;i<n;i++) ret[i] = head[i]*tail[i+2];
        return ret;
    }
};

五、和为k的子数组

1.链接

560. 和为 K 的子数组 - 力扣(LeetCode)

2.描述

3.思路

4.参考代码

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) 
    {
        int sum = 0;
        map<int,int> hash;
        int count = 0;
        for(int i = 0;i<nums.size();i++)
        {
            hash[sum]++;
            sum += nums[i];//此时sum为i位置的前缀和
            count += hash[sum-k];
        }
        return count;
    }
};

5.代码分析

六、和可被K整除的子数组

1.链接

974. 和可被 K 整除的子数组 - 力扣(LeetCode)

2.描述

3.思路

4.参考代码

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

七、连续数组

1.链接

525. 连续数组 - 力扣(LeetCode)

2.描述

3.思路

改题目若是将所有的0都换成-1,则依然和上一题的思路是一样的,都是通过前缀和去转化

4.参考代码

class Solution 
{
public:
    int findMaxLength(vector<int>& nums) 
    {
        unordered_map<int,int> hash;
        int ret = 0;
        int sum = 0;
        hash[0] = -1;
        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;
    }
};

八、矩阵区域和

1.链接

1314. 矩阵区域和 - 力扣(LeetCode)

2.描述

这题描述较复杂,大致意思就是,给你一个m*n的矩阵,并且给你一整数k,然后你得返回一个相同规模的矩阵,而这个矩阵内的数据要求是:

以【i,j】位置向上下左右各延伸k个单位然后围成的矩阵和,越界的部分视为0,例如:

3.思路

这题就是对二维前缀和的一个应用,对二维前缀和的表格建立和使用,需要熟练掌握分析,而不要去死记公式,得到二维前缀和表和得到使用方法后再根据这题进行分析,这里不重复分析,随便花个草图去分析即可

建表的递推公式:dp[i][j] = dp[i-1][j] + dp[i][j-1]  - dp[i-1][j-1] + mat[i][j]

使用表格的公式:dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1-1] + dp[x1-1][y1-1]

分析:

这题首先是如何确定x1、y1、x2、y2的问题,画图分析可得(太简单了略)

(x1,y1)    = (i-k,y-k)      , (x2,y2)  = (i+k,y+k)  

除了找到对应的矩阵区间,我们很容易想到还需要对边界条件进行除了,i-k和j-k是有可能越界的,因此最多我们不能让它们小于(0,0)的位置,i+k和j+k同理,最大不能大于(m-1,n-1)的位置

x1 = max(0,i-k);   y1 = max(0,y-k);     x2 = min(m-1,i+k);     y2 = min(n-1,j+k);

还有一个细节就是下标的映射关系要注意,因为在初始化dp表(前缀和表)时,我们会添加一个辅助位,而题目给的数组是从0开始的,dp表则是从1开始,所以写代码时要注意映射关系即可

4.参考代码

class Solution 
{
public:
    vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) 
    {
        int m = mat.size();
        int n = mat[0].size();
        vector<vector<int>> dp(m+1,vector<int>(n+1,0));
        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>> answer(m,vector<int>(n));
        for(int i = 0;i<m;i++)
        {
            for(int j = 0;j<n;j++)
            {
                int x1 = max(0,i-k), y1=max(0,j-k);
                int x2 = min(m-1,i+k), y2 = min(n-1,j+k);
                answer[i][j] = dp[x2+1][y2+1] - dp[x1][y2+1] - dp[x2+1][y1] + dp[x1][y1];//注意映射关系
            }
        }
        return answer;
    }
};

总结

本篇内容是关于前缀和的算法思想和应用,整理了一些经典的题目,从简单到难逐步递进,提供链接可以直接到力扣上做,也提供了描述可以直接通过看本篇文章去尝试思考解题,提供了参考思路和测试通过的代码(C++),整理学习下来后,个人认为一个是需要掌握一维和二维的表格建立和基本使用,还有相对较难的,但也有迹可循的一种用前缀和将题目转化,利用哈希表去优化效率的思想,参考五到七题,这个思路相对重要

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/517941.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于多模态单细胞数据构建共表达网络-MuSeGNN

本篇来自于MuSe-GNN: Learning Unified Gene Representation From Multimodal Biological Graph Data的补充材料。主要目的是从多模态数据中构建共表达网络。作者概述了使用CS-CORE&#xff0c;scTransform和SPARK-X进行预处理步骤和网络构建的算法细节。 目前存在大量用于图谱…

ESP32 引脚分配

请注意&#xff0c;以下引脚分配参考适用于流行的 30 引脚ESP32 devkit v1开发板。 仅输入引脚 GPIO34~39是GPIs–仅输入的管脚。这些引脚没有内部上拉或下拉电阻。它们不能用作输出&#xff0c;因此只能将这些管脚用作输入&#xff1a;GPIO 34、GPIO 35、GPIO 36、GPIO 39 S…

利用nginx-http-flv-module实现三种直播

目录 一、说明 二、目标 三、实现 四、直播地址 一、说明 此文在《流媒体服务器的搭建(支持hls)》《搭建nginx-http-flv-module直播系统》之后编写,很多详细内容需要参考它。 流媒体服务器的搭建(支持hls)

【解读Kubernetes架构】全面指南,带你掌握Kubernetes的设计原理与构成!

了解 Kubernetes 架构&#xff1a;综合指南 前言一、什么是 Kubernetes 架构&#xff1f;1.1、控制平面1.2、工作节点 二、Kubernetes 控制平面组件2.1、kube-api服务器2.2、etcd2.3、kube-scheduler2.4、Kube 控制器管理器2.5、云控制器管理器 &#xff08;CCM&#xff09; 三…

Web APIs简介 Dom

JS的组成 API API 是一些预先定义的函数&#xff0c;目的是提供应用程序与开发人员基于软件或硬件得以访问一组例程的能力&#xff0c;而又无需访问源码&#xff0c;或理解内部工作机制的细节 简单理解&#xff1a;API是给程序员提供的一种工具&#xff0c;以便能更轻松的实现…

车载电子电器架构 —— 工程EOL诊断

车载电子电器架构 —— 工程EOL诊断 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己…

数据的统计描述

data.info() 提供了关于数据框的简要摘要&#xff0c;包括&#xff1a; 索引类型数据列的数量非空值的数量&#xff08;针对每列&#xff09;每列的数据类型&#xff08;例如&#xff0c;int64, float64, object等&#xff09;内存使用情况提供了哪些列可能包含缺失值&#xff…

flink on yarn

前言 Apache Flink&#xff0c;作为大数据处理领域的璀璨明星&#xff0c;以其独特的流处理和批处理一体化模型&#xff0c;成为众多企业和开发者的首选。它不仅能够在处理无界数据流时展现出卓越的实时性能&#xff0c;还能在有界数据批处理上达到高效稳定的效果。本文将简要…

Linux文件IO(4):目录操作和文件属性获取

目录 1. 前言 2. 函数介绍 2.1 访问目录 – opendir 2.2 访问目录 – readdir 2.3 访问目录 – closedir 2.4 修改文件访问权限 – chmod/fchmod 2.5 获取文件属性 – stat/lstat/fstat 2.5.1 文件属性 – struct stat 2.6 文件类型 – st_mode 3. 代码练习 3.1 要求 3.2 代…

day04-MQ

1.初识MQ 1.1.同步和异步通讯 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就像打电话&#xff0c;需要实时响应。异步通讯&#xff1a;就像发邮件&#xff0c;不需要马上回复。 两种方式各有优劣&#xff0c;打电话可以立即得到响应&#xff0c;但是你…

数组--有序数组的平方

LeetCode中的第977题&#xff1a; 思想&#xff1a;①返回每个新数组&#xff1b;②排序&#xff1b; &#xff08;n个数&#xff0c;进行n-1趟比较。第j趟比较中要进行n-j次两两比较&#xff09; &#xff08;1&#xff09;n个数&#xff0c;进行n-1趟比较&#xff1a; for(…

深度学习【向量化(array)】

为什么要向量化 在深度学习安全领域、深度学习练习中&#xff0c;你经常发现在训练大量数据时&#xff0c;深度学习算法表现才更加优越&#xff0c;所以你的代码运行的非常快至关重要&#xff0c;否则&#xff0c;你将要等待非常长的时间去得到结果。所以在深度学习领域向量化…

(源码+部署+讲解)基于Spring Boot和Vue的宠物领养系统的设计与实现

一、引言 本报告旨在详细描述基于Spring Boot后端框架和Vue前端框架的宠物领养系统的设计与实现过程。宠物领养系统旨在为宠物主人和领养者提供一个便捷的平台&#xff0c;实现宠物的信息发布、领养申请、信息管理等功能。通过该系统&#xff0c;宠物主人可以快速找到适合的领养…

Kali WSL2(windows下安装了kali)

自从WSL2以来&#xff0c;感觉各方面也挺好的&#xff0c;有时候比vmware workstation方便&#xff0c;特别单独使用一个linux的时候。所以研究了下kali&#xff0c;也是很OK的&#xff0c;以及验证完成了。 本文参考官网&#xff1a; Kali Linux | Penetration Testing and Et…

C++——特殊类设计

目录 前言 一&#xff0c;请设计一个不能被拷贝的类 二&#xff0c;请设计一个只能在堆上创建对象的类 2.1 思路一&#xff1a;构造函数私有 2.2 思路二&#xff0c;析构函数私有 三&#xff0c;请设计一个只能在栈上创建对象的类 四&#xff0c;请设计一个只能创建一个…

练习14 Web [极客大挑战 2019]Upload

phtml格式绕过&#xff0c;burp修改content-type绕过&#xff0c;常见的文件上传存放目录名 题目就叫upload&#xff0c;打开靶机 直接上传一个图片格式的一句话木马&#xff0c;返回如下&#xff1a; 提交练习5和9中的两种可以执行图片格式php代码的文件&#xff0c;修改con…

Netty的线程模型

文章目录 前言NIO的三种reactor模式Netty对3种Reactor模式的支持Netty内部如何实现Reactor线程模型在事件分发过程中&#xff0c;Netty如何避免竞争条件 前言 Netty的线程模型采用了Reactor模式&#xff0c;这是一种高性能的IO设计模式&#xff0c;它基于NIO多路复用。Netty支…

226.翻转二叉树

226.翻转二叉树 力扣题目链接(opens new window) 翻转一棵二叉树。 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNo…

Linux操作系统之防火墙、redis安装

目录 一、防火墙 1、防火墙的类别 2、安装iptables(四表五链&#xff09; 一、防火墙 1、防火墙的类别 安全产品 杀毒 针对病毒&#xff0c;特征篡改系统中文件杀毒软件针对处理病毒程序 防火墙 针对木马&#xff0c;特征系统窃密 防火墙针对处理木马 防火墙分为两种 硬件…

MySql 实战大数据查询-(表分区实现)

一 mysql分区&#xff1a; 分区是将单个表按照某种规则划分成多个子集&#xff0c;每个子集称为一个分区。常见的分区策略包括按照时间范围、范围值、列表等进行分区。 优点&#xff1a; 查询性能更好&#xff0c;涉及分区键的查询&#xff0c;数据库引擎可以只扫描特定分区&…