【前缀和】算法实战

news2025/6/27 10:30:32

文章目录

  • 一、算法原理
    • 1. 一维前缀和
    • 2. 二维前缀和
  • 二、算法实战
    • 1. leetcode560 和为K的子数组
    • 2. leetcode974 和可被K整除的子数组
    • 3. leetcode525 连续数组
    • 4. leetcode1314 矩阵区域和
    • 5. leetcode724 寻找数组的中心下标
    • 6. leetcode238 除自身以外数组的乘积
  • 三、总结


一、算法原理

前缀和算法指的是某段序列的前n项和,前缀和一般用来求数组中某段连续区间的和。下面我们来看一下它的算法原理。

1. 一维前缀和

在这里插入图片描述
前缀和

思路:

先预处理一个前缀和数组:dp[i] 表示 [1, i] 区间内所有元素的和。dp数组的递推公式为:dp[i] = dp[i - 1] + arr[i];这里我们的数组下标从1开始是为了处理边界情况。

使用前缀和数组:[ l , r ]区间和 = dp[ r ] - dp[ l - 1 ];

代码实现:

#include <iostream>
using namespace std;
typedef long long LL;
const int N = 101010;
int arr[N];
LL dp[N];

int main() 
{
    int n, m;
    cin >> n >> m;

    for(int i = 1; i <= n; i++)
        cin >> arr[i];
    
    for(int i = 1; i <= n; i++)
        dp[i] = dp[i - 1] + arr[i];
    while(m--)
    {
        int l, r;
        cin >> l >> r;
        printf("%lld\n", dp[r] - dp[l - 1]);
    }
    return 0;
}

2. 二维前缀和

在这里插入图片描述
二维前缀和

思路:

类⽐于⼀维数组的形式,如果我们能处理出来从 [0, 0] 位置到 [i, j] 位置这⽚区域内所有元素的累加和,就可以在 O(1) 的时间内,搞定矩阵内任意区域内所有元素的累加和。因此我们接下来仅需完成两步即可:

  • 第⼀步:搞出来前缀和矩阵
    这⾥就要⽤到⼀维数组⾥⾯的拓展知识,我们要在矩阵的最上⾯和最左边添加上⼀⾏和⼀列,这样我们就可以省去⾮常多的边界条件的处理(同学们可以⾃⾏尝试直接搞出来前缀和矩阵,边界条件的处理会让你崩溃的)。处理后的矩阵就像这样:
    在这里插入图片描述
    这样,我们填写前缀和矩阵数组的时候,下标直接从 1 开始,能⼤胆使⽤【i - 1 , j - 1】 位置的值。
  • 递推方程
    递推⽅程就是:sum[i][j]=sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1]+matrix[i - 1][j - 1]
  • 使用前缀和矩阵:
    在这里插入图片描述
    目标矩阵的面积 = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1]

代码实现:

#include <iostream>
#include <vector>
using namespace std;
const int N = 100010;

int n, m, q;

int main()
{
    scanf("%d%d%d", &n, &m, &q);
    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 ++ )
            scanf("%lld", &dp[i][j]);

    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];

    while (q -- )
    {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        printf("%lld\n", dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1]);
    }

    return 0;
}

二、算法实战

1. leetcode560 和为K的子数组

在这里插入图片描述
和为K的子数组

解题思路:前缀和 + 哈希表

在这里插入图片描述

我们可以将这个问题的求解转换一下,这道题的原意是让我们求以i为结尾的所有满足要求的子数组,我们可以将问题转换为:在[0, i - 1]区间内,有多少前缀和等于sum[i] - k

我们建立哈希表,以和为键,出现的次数为对应的值,记录prev[i]出现的次数,那么以 i 结尾的答案 mp[pre[i]−k] 即可在 O(1) 时间内得到。由于prev[i]的计算只与前一项的答案有关,因此我们直接用prev变量来记录答案即可。

这里有一个细节问题,如果整个前缀和等于K需要特殊处理一下,我们需要提前让hash表中的hash[0] = 1;

代码实现:

// 写法一:
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        unordered_map<int,int> hash;
        int pre = 0, cnt = 0;
        hash[0] = 1;
        for(auto& e : nums)
        {
            pre += e;
            if(hash.find(pre - k) != hash.end())
                cnt += hash[pre - k];
            hash[pre]++;
        }
        return cnt;
    }
};
// 写法二:
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> pre(n + 1);
        for(int i = 1; i <= n; i++)
            pre[i] = pre[i - 1] + nums[i - 1];
        unordered_map<int,int> hash;
        int res = 0;
        hash.insert({0, 1});
        for(int i = 1; i <= n; i++)
        {
            res += hash[pre[i] - k];
            hash[pre[i]]++;
        }
        return res;
    }
};

2. leetcode974 和可被K整除的子数组

在这里插入图片描述
和可被K整除的子数组

补充知识:同余定理+(C++、Java)中[负数%正数]的结果以及修正

在这里插入图片描述

解题思路:前缀和+哈希表

这道题和上一道题目很相似。一个是求和为k的子数组,一个是求和可被k整除的子数组,方法是一样的。首先使用pre变量来遍历数组存储到目前为止的前缀和,根据同余定理,只需要满足pre[j] mod K == pre[i-1] mod K即可。

我们可以考虑对数组进行遍历,在遍历同时统计答案。当我们遍历到第 i 个元素时,我们统计以 ji 结尾的符合条件的子数组个数。然后维护一个以前缀和模 K 的值为键,出现次数为值的哈希表 ,在遍历的同时进行更新。这样类似上一题进行维护和计算即可得到结果。

代码实现:

class Solution {
public:
    int subarraysDivByK(vector<int>& nums, int k) {
        unordered_map<int,int> hash;
        int pre = 0, cnt = 0;
        hash[0] = 1;
        for(auto& e : nums)
        {
            pre += e;
            int r = ((pre - k) % k + k) % k;
            if(hash.find(r) != hash.end())
                cnt += hash[r];
            hash[r]++;
        }
        return cnt;
    }
};

3. leetcode525 连续数组

在这里插入图片描述
连续数组

解题思路:前缀和+哈希表

根据题意可得,数组中的元素不是0就是1,题目要求我们找到含有相同数量的 0 和 1 的最长连续子数组,那么我们可以转化一下思路:1. 将所有的0修改为-1。2. 在数组中找出最长的子数组,使数组中所有元素的和为0

这里我们还是使用前缀和+哈希表的方式来解决。在哈希表中存的是前缀和的末尾元素的下标。当哈希表中有重复的<sum,i>时,只保留前面那一对<sum,i>,当判断某一对前缀和在哈希表中出现过时,说明【后者-前者】区间内的元素之和为0。这时,我们使用一个Max变量来更新我们的结果即可。当然,区间长度=[当前下标-前一个前缀和等于0的末尾元素下标],否者则将该前缀和的末尾元素下标丢入哈希表即可。这保证了我们如果有重复的<sum,i>,只存储前面那一对<sum,i>。当然别忘了,如果遇到数组中所有元素之和等于0,这种情况我们需要特殊处理一下,就是将hash[0]=-1

代码实现:

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

4. leetcode1314 矩阵区域和

在这里插入图片描述
矩阵区域和

解题思路:二维前缀和

首先我们需要读懂题目的含义,我们需要将原数组的每个元素变成对应矩阵范围的区域和,为了能够快速求出矩阵某段区域的面积之和。因此我们需要构造前缀和数组。

因为我们所学习的前缀和数组下表是从1开始的,但是在力扣中的数组的下标都是从0开始的,所以我们构造前缀和数组时需要将前缀和数组在原数组的基础上添加一行一列,以便于后续好处理。

但是这道题目真正的难点在于下标之间的关系,因为前缀和数组的下标是从1开始的,但是我们所求的目标数组又是从0开始的,所以这之间就必须要存在一种转换关系,需要我们自己把握。

代码实现:

class Solution {
public:
    int getRet(int x1, int x2, int y1, int y2)
    {
        return tmp[x2][y2] - tmp[x1 - 1][y2] - tmp[x2][y1 - 1] + tmp[x1 - 1][y1 - 1];
    }
    vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {
        int n = mat.size(), m = mat[0].size();
        tmp = vector<vector<int>>(n + 1, vector<int>(m + 1));

        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                tmp[i][j] = tmp[i - 1][j] + tmp[i][j - 1] - tmp[i - 1][j - 1] + mat[i - 1][j - 1];
        
        // 目标数组
        vector<vector<int>> ret(n, vector<int>(m));
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++){
                int x1 = max(i - k, 1);
                int x2 = min(i + k, n);
                int y1 = max(j - k, 1);
                int y2 = min(j + k, m);
                ret[i - 1][j - 1] = getRet(x1, x2, y1, y2);
            }
        return ret;
    }
private:
    vector<vector<int>> tmp; // 前缀和数组
};

5. leetcode724 寻找数组的中心下标

在这里插入图片描述
寻找数组的中心下标

解题思路:

题目要求我们求解数组的中心下标,中心下标的定义为:其左侧所有元素相加的和等于右侧所有元素相加的和。根据题目的这个意思,我们可以构建前缀和数组。我们可以根据:左侧元素相加之和=dp[n - 1] - dp[i],右侧元素相加之和=dp[i-1],我们使用左侧元素减去右侧元素之和,看结果是否为0,如果为零,该位置则为数组的中心下标。当然我们需要注意边界情况特殊处理一下即可。

代码实现:

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

    }
};

6. leetcode238 除自身以外数组的乘积

在这里插入图片描述
除自身以外数组的乘积

解题思路:

这道题的要求是让我们将数组中的每个元素变成除了它自身外其他元素之积,我们可以根据前缀和的思想,构造两个数组:前缀积数组和后缀积数组。前缀积数组: f[i]表示 [0, i-1] 区间内所有元素的乘积。后缀积数组: g[i]表示 [i+1, n-1] 区间内所有元素的乘积。我们求目标数组的元素时直接 ret[i] = f[i] * g[i] 即可。

当然这道题目我们还可以使用滚动数组进行优化,原本需要开三个数组,但是优化后只需要一个数组就可以解决。先开一个前缀积数组,然后求出前缀积。然后接下来for循环从后往前遍历,因为后缀积数组的后一项可以推出前一项,所以使用一个临时变量right来依次滚动推导每一次需要使用的后缀积,然后乘以前缀积数组中的元素,就可以直接将前缀积数组推导成为目标数组。

代码实现:

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

三、总结

在算法题中,前缀和计算时间复杂度为O(n),后续要查询对应的操作算法复杂度为O(1),使用前缀和后,能大大减少算法复杂度。同时,前缀和属于「空间换时间」的算法或者也可以说不是算法,而是一种数组的「预处理」


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

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

相关文章

基于Python的高校学生成绩分析系统

随着计算机技术发展&#xff0c;计算机系统的应用已延伸到社会的各个领域&#xff0c;大量基于网络的广泛应用给生活带来了十分的便利。所以把高校成绩分析与现在网络相结合&#xff0c;利用计算机搭建高校成绩分析系统&#xff0c;实现高校成绩分析的信息化。则对于进一步提高…

gdb调试core dump

gdb调试core dump 文章目录 gdb调试core dumpgdb core dump调试步骤Segmentation faultcore dump**coredump文件的存储位置**apport手动指定存储位置 开启coredump测试readelf 查看core dump文件信息gdb查看core文件总结Reference>>>>> 欢迎关注公众号【三戒纪元…

P17~P18 电路定理 电路中难得的精彩到极致的电路理论

特勒根定理、互易定理、对偶定理比较难&#xff0c;非常重要&#xff0c;因为他们可以解决其他定理无法解决的问题。 1、特勒根定理1——个人感觉像能量守恒 特勒根定理与基尔霍夫定理齐名&#xff0c;与拓扑结构有关。都适用于任何线性非线性&#xff0c;时变的非时变的元件…

使用zoom预览出图和系统相机预览出图,画质不一样的问题分析

1、问题背景 最近在基于 Android 的平台调试一款摄像头&#xff0c;客户有反馈一个问题&#xff0c;系统自带的 Camera2 app 预览出图是正常的&#xff0c;但用 Zoom app 打开摄像头&#xff0c;出图画面存在畸变、锯齿、过曝的问题&#xff0c;现象如下图所示。 2、问题分析 …

前端基础(Web API)

目录 前言 Web API DOM 基本概念 查找元素 document.getElementById document.getElementsByClassName document.evaluate() 修改元素 添加元素 修改元素 复制元素 删除元素 Event事件 事件创建 常用的事件 监听事件 click事件 mouseover事件 事件绑定…

LTPP在线开发平台【使用教程】

LTPP在线开发平台 点击访问 LTPP在线开发平台 LTPP&#xff08;Learning teaching practice platform&#xff09;在线开发平台是一个编程学习网站&#xff0c;该网站集文章学习、短视频、在线直播、代码训练、在线问答、在线聊天和在线商店于一体&#xff0c;专注于提升用户编…

使用SpringBoot+SpringMVC+Mybatis+WebSocket实现云聊天项目

云聊天 1. 项目介绍 本项目是仿照微信实现网页版聊天程序&#xff0c;用户注册登录后可与在线好友实时聊天&#xff0c;下线好友上线后可以查看到好友发送的消息&#xff1b;用户可以在搜索框搜索用户添加好友&#xff1b;用户还可以查看好友申请列表&#xff0c;选择是否同意…

Golang使用消息队列(RabbitMQ)

最近在使用Golang做了一个网盘项目&#xff08;类似百度网盘&#xff09;&#xff0c;这个网盘项目有一个功能描述如下&#xff1a;用户会删除一个文件到垃圾回收站&#xff0c;回收站的文件有一个时间期限&#xff0c;比如24h&#xff0c;24h后数据库中记录和oss中文件会被删除…

使用Vscode 编辑器 导出、导入和运行Excel中的VBA代码

使用Vscode 编辑器 导出、导入和运行Excel中的VBA代码 前言 Excel自带的 Microsoft Visual Basic for Applications 编辑器常被人称为上古编辑器&#xff0c;的确不适合代码编辑&#xff0c;这是其一&#xff0c;其二是当系统语言与Excel的安装语言不一致时&#xff0c;往往出现…

QChart类用来 管理 图表的:数据序列(series)、图例(legend)和坐标轴(axis)

QChart类用来 管理 图表的&#xff1a;数据序列&#xff08;series&#xff09;、图例&#xff08;legend&#xff09;和坐标轴&#xff08;axis&#xff09; 1、数据序列类 继承关系 2、坐标轴类 的继承关系 3、图例类 什么是图例&#xff1f; 图例&#xff1a;是集中于地图…

Docker搭建LNMP运行Wordpress平台

一、项目1.1 项目环境1.2 服务器环境1.3 任务需求 二、Linux 系统基础镜像三、Nginx1、建立工作目录2、编写 Dockerfile 脚本3、准备 nginx.conf 配置文件4、生成镜像5、创建自定义网络6、启动镜像容器7、验证 nginx 四、Mysql1、建立工作目录2、编写 Dockerfile3、准备 my.cnf…

如何做H5性能测试?

提起H5性能测试&#xff0c;可能许多同学有所耳闻&#xff0c;但是不知道该如何对H5做性能测试&#xff0c;或者不知道H5应该关注哪些性能指标。今天我们就来看下&#xff0c;希望阅读本文后&#xff0c;能够有所了解。 常用指标 1、H5性能相关参数介绍 白屏时间&#xff1a;…

FRP内网穿透,配置本地电脑作为服务器

FRP内网穿透&#xff0c;配置本地电脑作为服务器 下载FRP服务端客户端 参考链接&#xff1a; https://www.it235.com/实用工具/内网穿透/pierce.html https://www.cnblogs.com/007sx/p/17469301.html 由于没有公网ip&#xff0c;所以尝试内网穿透将本地电脑作为服务器&#xff…

第 6 章 递归(1)(应用场景,概念,调用机制,解决问题类型,重要规则)

6.1递归应用场景 看个实际应用场景&#xff0c;迷宫问题(回溯)&#xff0c; 递归(Recursion) 6.2递归的概念 简单的说: 递归就是方法自己调用自己,每次调用时传入不同的变量.递归有助于编程者解决复杂的问题,同时可以让代码变得简洁。 6.3递归调用机制 我列举两个小案例,…

代码随想录算法训练营之JAVA|第三十四天|509. 斐波那契数

今天是第 天刷leetcode&#xff0c;立个flag&#xff0c;打卡60天&#xff0c;如果做不到&#xff0c;完成一件评论区点赞最高的挑战。 算法挑战链接 509. 斐波那契数https://leetcode.cn/problems/fibonacci-number/ 第一想法 这个就是求斐波那契数&#xff0c;感觉应该不用…

Error creating bean with name ‘esUtils‘ defined in file

报错异常&#xff1a; 背景&#xff1a; esUtils在common服务中、启动media服务时候、报这个异常、后排查esUtils在启动时候发生异常引起的、在相关bean中加入try{}catch{}即可解决问题 String[] split url.split(","); HttpHost[] httpHosts new HttpHost[split.…

卷积网络手动实现和nn实现

代码中涉及的图片实验数据下载地址&#xff1a;https://download.csdn.net/download/m0_37567738/88235543?spm1001.2014.3001.5501 &#xff08;一&#xff09;手动实现卷积算法 代码&#xff1a; import os import torch.nn.functional as F from PIL import Image import…

装饰器读取不到被装饰函数的参数-已解决

def write_case_log(func):def wrapper(*args, **kwargs):logger.info("{}开始执行".format(func.__name__))func(*args,**kwargs)logger.info("{}执行中".format(args))logger.info("{}执行结束",format(func.__name__))return wrapper被装饰函…

卡方分箱(chi-square)

统计学&#xff0c;风控建模经常遇到卡方分箱算法ChiMerge。卡方分箱在金融信贷风控领域是逻辑回归评分卡的核心&#xff0c;让分箱具有统计学意义&#xff08;单调性&#xff09;。卡方分箱在生物医药领域可以比较两种药物或两组病人是否具有显著区别。但很多建模人员搞不清楚…

基于SSM的校园旧书交易交换网站

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…