C++算法 —— 前缀和

news2025/1/13 17:26:42

一、【模版】前缀和

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++),整理学习下来后,个人认为一个是需要掌握一维和二维的表格建立和基本使用,还有相对较难的,但也有迹可循的一种用前缀和将题目转化,利用哈希表去优化效率的思想,参考五到七题,这个思路相对重要

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

20.2k stars项目搭建私人网盘界面美功能全

Nextcloud是一套用于创建网络硬盘的客户端&#xff0d;服务器软件。其功能与Dropbox相近&#xff0c;但Nextcloud是自由及开放源代码软件&#xff0c;每个人都可以在私人服务器上安装并执行它。 GitHub数据 20.2k stars561 watching3.2k forks 开源地址:https://github.com/ne…

保研线性代数复习3

一.基底&#xff08;Basis&#xff09; 1.什么是生成集&#xff08;Generating Set&#xff09;&#xff1f;什么是张成空间&#xff08;Span&#xff09;&#xff1f; 存在向量空间V(V&#xff0c;&#xff0c;*)&#xff0c;和向量集&#xff08;xi是所说的列向量&#xff…

H5面临的网络安全威胁和防范措施

H5&#xff0c;是基于HTML5技术的网页文件。HTML&#xff0c;全称Hyper Text Markup Language&#xff0c;即超文本标记语言&#xff0c;由Web的发明者Tim Berners-Lee与同事Daniel W. Connolly共同创立。作为SGML的一种应用&#xff0c;HTML编写的超文本文档能够独立于各种操作…

《机器学习算法面试宝典》正式发布!

大家好&#xff0c;历时半年的梳理和修改&#xff0c;《机器学习算法面试宝典》&#xff08;以下简称《算法面试宝典》&#xff09;终于可以跟大家见面了。 近年来&#xff0c;很多理科专业学生也纷纷转入算法赛道&#xff0c;特别是最近 ChatGPT 的爆火&#xff0c;推动了AI …

服务器硬件构成与性能要点:CPU、内存、硬盘、RAID、网络接口卡等关键组件的基础知识总结

文章目录 服务器硬件基础知识1. CPU&#xff08;中央处理器&#xff09;2. 内存&#xff08;RAM&#xff09;3. 硬盘4. RAID&#xff08;磁盘阵列&#xff09;5. 网络接口卡&#xff08;NIC&#xff09;6. 电源7. 散热器8. 主板9. 显卡10. 光驱 服务器硬件基础知识 服务器是一…

JDK类加载器剖析

0.前言 我之所以深入研究 Java 类加载器&#xff0c;是为了解决一个奇怪的问题。流行出版物&#xff0c;也就是人们所认为的 Java 世界的灯塔&#xff0c;充斥着关于这个主题的相互矛盾和过时的信息。这种矛盾引发了我的调查 — — 在 Java 类加载器的迷宫中寻求清晰的答案。 …

Oracle 数据库中的全文搜索

Oracle 数据库中的全文搜索 0. 引言1. 整体流程2. 创建索引2-1. 创建一个简单的表2-2. 创建文本索引2-3. 查看创建的基础表 3. 运行查询3-1. 运行文本查询3-2. CONTAINS 运算符3-3. 混合查询3-4. OR 查询3-5. 通配符3-6. 短语搜索3-7. 模糊搜索&#xff08;Fuzzy searches&…

【Servlet】Servlet入门

文章目录 一、介绍二、入门案例导入servlet-api的解决办法 一、介绍 概念&#xff1a;server applet&#xff0c;即&#xff1a;运行在服务器端的小程序 Servlet就是一个接口&#xff0c;定义了Java类被浏览器访问到&#xff08;tomcat识别&#xff09;的规则。 将来我们定义…

java数据结构与算法刷题-----LeetCode504. 七进制数

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 1. 倒退迭代&#xff08;除基取余法&#xff09;2. 省略掉反转操…

【JavaEE初阶系列】——文件操作 IO 之 文件系统操作

目录 &#x1f4dd;认识文件 &#x1f6a9;树型结构组织 和 目录 &#x1f388;绝对路径和相对路径 &#x1f6a9;文件类型 &#x1f4dd;文件系统操作 &#x1f388;File 概述 &#x1f388;File类的使用 1. 绝对路径 vs 相对路径 2. 路径分隔符 3. 静态成员变量 4…

小明记账簿-记账工具

今天不了技术&#xff0c;聊一下工具&#xff0c;最近耗费无数个日日夜夜&#xff0c;做了一个记账小程序&#xff0c;感觉很实用&#xff0c;简单方便&#xff0c;希望帮助那些需要帮助的人。 想轻松管理个人财务吗&#xff1f;试试小明记账簿吧&#xff01;它是一款便捷&…

篮桥杯刷题第n天(dp更新)

最长上升子序列 输入&#xff1a; 10 1 4 5 1 4 1 9 1 9 输出&#xff1a; 4 算法思想&#xff1a;记录每个数为结尾的最长子序列长度&#xff0c;作为dp数组。 eg&#xff1a;&#xff08;这个是以每个位置开头的记录最长的来穷举但核心仍为上述的算法思想&#xff09; …

vue快速入门(一)vue的导入方法

注释很详细&#xff0c;直接上代码 新增内容 下载js代码导入实例数据绑定显示 源码 index.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-widt…

【解读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; 三…

【HarmonyOS】ArkUI - 动画

利用属性动画、显示动画、组件转场动画实现组件动画效果。 一、属性动画 属性动画是通过设置组件的 animation 属性来给组件添加动画&#xff0c;当组件的 width、height、Opacity、backgroundColor、scale、rotate、translate 等属性变更时&#xff0c;可以实现渐变过渡效果。…

有人用GPT来做日内交易,居然赚钱了!但是……

在我们还在烦恼会不会被AI替代时&#xff0c;已经有人在教ChatGPT去炒股票了。 在近年ChatGPT火速出圈后&#xff0c;围绕AI能取代什么职业的讨论持续受到大众关注。 从事客服、编程、法律合规以及内容创作等行业人员最早感受到这股AI带来的寒意。 那ChatGPT能不能替代交易员…

Web APIs简介 Dom

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

[计算机知识] 各种小问题思考

哈希算法以及哈希冲突 哈希算法&#xff1a;将任何长度的输入通过散列函数转换成固定长度的字符串 哈希冲突&#xff1a;不同的输入经过哈希函数处理后得到相同的哈希值 因为哈希函数的输出域是有限的 解决哈希冲突&#xff1a; 1. 开放寻址&#xff1a;产生哈希冲突后&…

刷题之Leetcode704题(超级详细)

704. 二分查找 力扣题目链接(opens new window)https://leetcode.cn/problems/binary-search/ 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&am…

【Apache Doris】周FAQ集锦:第 1 期

【Apache Doris】周FAQ集锦&#xff1a;第 1 期 SQL问题数据操作问题运维常见问题其它问题关于社区 欢迎查阅本周的 Apache Doris 社区 FAQ 栏目&#xff01; 在这个栏目中&#xff0c;每周将筛选社区反馈的热门问题和话题&#xff0c;重点回答并进行深入探讨。旨在为广大用户和…