【算法】一维、二维前缀和 解决算法题(C++)

文章目录

  • 1. 前缀和算法 介绍
  • 2. 一维前缀和 模板引入
    • DP34【模板】前缀和
  • 3. 利用一维前缀和 解题
    • 724.寻找数组的中心下标
    • 238.除自身以外数组的乘积
    • 560.和为K的子数组
    • 974.和可被K整除的子数组
    • 525.连续数组
  • 二维前缀和 模板
    • 1314.矩阵区域和

1. 前缀和算法 介绍

前缀和算法 用于高效地计算 数组或序列 中某个区间内元素的和。

前缀和数组是一个辅助数组,其每个元素存储原始数组从开头到当前位置的元素和。通过提前计算前缀和数组,可以在O(1)的时间复杂度内快速计算出任意区间内的元素和。

2. 一维前缀和 模板引入

DP34【模板】前缀和

在这里插入图片描述

这道题目帮助我们 理解前缀和模板使用前缀和数组

思路

  • 题意分析:题目要求我们返回 数组中l~r范围内所有元素的和

  • 我们引出前缀和数组dp的使用:
    在这里插入图片描述

  • 解法:前缀和数组

    1. 我们首先通过循环dp[i] = dp[i-1] + arr[i] 进行dp数组的初始化(预处理)
    2. 再根据找到的规律,l~r的范围和即为dp[r]-dp[l-1]

代码

int main() {
    int n = 0, q = 0;
    cin >> n >> q;
    vector<int> arr(n+1); // 下标从1开始,数组大小为n+1
    // 写入数组
    for(int i = 1; i <= n; ++i) cin >> arr[i];

    // 预处理前缀和数组
    vector<long long> dp(n+1); // long long 防止溢出
    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;
}

3. 利用一维前缀和 解题

724.寻找数组的中心下标

在这里插入图片描述

思路

在这里插入图片描述

  • 题意分析:要求我们找到数组的中心下标,中心下标满足:左侧元素和==右侧元素和
  • 解法:前缀和数组 + 后缀和数组
    1. 预处理前缀和 / 后缀和数组
      • p[i] = p[i-1] + nums[i-1];
      • s[i] = s[i+1] + nums[i+1];
    2. 遍历数组:找到满足条件的中心下标(p[i] == s[i])
    3. 细节注意:关于创建两数组时,循环条件从哪到哪。

代码

int pivotIndex(vector<int>& nums) {
    int n = nums.size();
    
    // 预处理前缀和数组
    vector<int> p(n); // P[i]:[0, i-1] 之间所有元素之和; 
    for(int i = 1; i < n; ++i) // p[0] == 0 s[n-1] == 0]
        p[i] = p[i-1] + nums[i-1];

    // 预处理后缀和数组
    vector<int> s(n); // s[i]:[i+1, n-1] 之间所有元素之和
    for(int i = n - 2; i >= 0; i--)
        s[i] = s[i+1] + nums[i+1];

    // 通过前缀和/后缀和数组找到中心下标
    int i;
    for(i = 0; i <= n - 1; ++i)
    {
        if(p[i] == s[i])
            return i;
    }

    return -1;
}

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

在这里插入图片描述

思路

  • 题意分析:返回数组answer,answer[i]为nums中除去自身的其余元素乘积。
  • 解法:前缀积数组 + 后缀积数组
    1. 我们知道:不论是前缀和还是前缀积数组,p[i]的值代表0~i-1位置的和/积,不包括其自身
    2. 则当我们求出数组的前缀积和后缀积后,answer[i] 即为 p[i] * s[i]。

在这里插入图片描述

代码

vector<int> productExceptSelf(vector<int>& nums) {
    int n = nums.size();
    vector<int> p(n), s(n);

    p[0] = s[n-1] = 1; // 边界条件
    // 构建前缀积数组
    for(int i = 1; i <= n - 1; ++i)
        p[i] = p[i-1] * nums[i-1];

    // 构建后缀积数组
    for(int i = n-2; i >= 0; --i)
        s[i] = s[i+1] * nums[i+1];

    vector<int> answer(n);
    for(int i = 0; i < n; ++i)
        answer[i] = p[i] * s[i];

    return answer;
}

560.和为K的子数组

思路

  • 题意分析:题目要求找到和为k的子数组的个数
  • 解法一:暴力枚举
    1. 一道题如果没有思路,首先可以想暴力解法
    2. 即:用两个循环遍历所有子数组,找到和为k的
    3. 缺点:时间开销大,时间复杂度O(n^2)
  • 为什么不能使用双指针(滑动窗口)?
    • 我们知道,双指针适合解决子数组问题,但其是解决连续子数组,这道题满足要求的子数组不一定连续。且数组中存在负数,也不能利用单调性使用滑动窗口。
  • 解法二:前缀和 + 哈希表
    在这里插入图片描述
    • 如上图所示

代码

int subarraySum(vector<int>& nums, int k) {
    int sum = 0, count = 0; // sum存储前缀和
    unordered_map<int, int> hash;
    hash[0] = 1; // 特殊情况:当子数组的第一个元素就满足条件的情况时
    for(int x : nums)
    {
        sum += x;
        // 以x为结尾的子数组,值为sum-k,则存在满足和为k的子数组
        if(hash.count(sum-k)) count += hash[sum-k];
        hash[sum]++;
    }

    return count;
}

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

在这里插入图片描述

思路

  • 题意分析:此题和前一题很像,只是从求和为k的子数组变成求和可被k整除的子数组的个数
  • 解法:前缀和 + 哈希表
    1. 思路与之前一致,我们每次在更新前缀和sum后,更新余数r,如果哈希表中存在则更新结果
  • 细节注意:同理为了防止前缀和为0的子数组满足条件,则将hash[0%k](就是0)定为1

代码

int subarraysDivByK(vector<int>& nums, int k) {
    // C++,java 中负数%正数=负数
    // 为了使余数满足题目条件,余数计算为(sum % k + k) % k
    int sum = 0, count = 0;
    unordered_map<int, int> hash;
    hash[0 % k] = 1; // hash[0] = 1;
    for(int x : nums)
    {
        sum += x;
        int r = (sum % k + k) % k;
        if(hash.count(r)) count += hash[r];
        hash[r]++;
    }

    return count;
}

525.连续数组

在这里插入图片描述

思路

  • 题意分析:要求找到有相同数量0和1的最长子数组
  • 这道题要求我们找到最长连续子数组,但是没有直接单调性,不能使用滑动窗口解题
  • 将数组中的1全部改为0,题目就转化为了:找和为0的连续最长子数组
    • 相当于将和为K的子数组改为了和为0的子数组 ,但需要注意的是,这道题要求的是最长子数组的长度:所以我们用哈希表分别存储前缀和+下标
  • 解法:前缀和 + 哈希表
    • 我们每次将前缀和加入到数组中,如果已经存在,则根据长度更新结果
    • 否则将当前下标与前缀和加入到hash中
  • 细节注意:因为我们计算长度是用:下标i-hash[sum]
    • 如果首位就是满足条件的,此时长度应为1
    • 即i - hash[0] = 1,此时i为0,为了保证特殊情况,我们将hash[0]设为-1

在这里插入图片描述

代码

int findMaxLength(vector<int>& nums) {
    int sum = 0, ret = 0;
    // 将数组中的0改为-1,题目可以演化为:求和为0的子数组
    //for(int &x : nums)  x = 0 ? -1 : x;
    unordered_map<int, int> hash; // 哈希表存放前缀和以及下标
    hash[0] = -1;
    for(int i = 0; i < nums.size(); ++i){
        sum += nums[i] == 0 ? -1 : 1; // 更新前缀和
        if(hash.count(sum)) // 前缀和sum存在 则更新ret(hash[sum] 为前缀和尾部下标, i-hash[sum] 为 连续数组长度)
            ret = max(ret, i - hash[sum]);
        else
            hash[sum] = i;
    }

    return ret;
}

二维前缀和 模板

1314.矩阵区域和

在这里插入图片描述

思路

  • 题意分析:题目要求返回answer矩阵,矩阵每一位元素可以理解为是以mat的每一位为中心,向上下左右分别扩展k个单位的元素总和。

  • 解法:二维前缀和
    在这里插入图片描述

    在这里插入图片描述

    1. 根据上面的图,我们首先用两层循环预处理前缀和矩阵
    2. 随后使用前缀和矩阵:只需要根据当前的(i, j)下标找到其向四周扩散的矩阵的左上和右下的坐标即可
    3. 根据求得的(x1, y1) (x2, y2) 以及我们算出的公式计算结果
  • 需要注意的是,最好不要死记模板公式,理解了过程,做题的时候可以模拟,自然会想出来过程

代码

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] + mat[i-1][j-1] - dp[i-1][j-1];

    // 使用前缀和矩阵 构建answer
    vector<vector<int>> answer(m, vector<int>(n));
    for(int i = 0; i < m; ++i)
    {
        for(int j = 0; j < n; ++j)
        {
            // answer[0][0] 对应 dp[1][1],把坐标+1
            int x1 = max(i-k, 0) + 1, y1= max(j-k, 0) + 1;
            int x2 = min(i+k, m-1) + 1, y2 = min(j+k, n-1) + 1;
            answer[i][j] = dp[x2][y2] - dp[x2][y1-1] - dp[x1-1][y2] + dp[x1-1][y1-1];
        }
    }

    return answer;
}

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

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

相关文章

白话机器学习的数学-3-评估

1、 模型评估 那我们如何测量预测函数 fθ(x)的正确性&#xff0c;也就是精度呢&#xff1f; 观察函数的图形&#xff0c;看它能否很好地拟合训练数据&#xff1a; 这是只有一个变量的简单问题&#xff0c;所以才能在图上展 示出来。 过像多重回归这样的问题&#xff0c;变量增…

x-cmd pkg | bit - 实验性的现代化 git CLI

目录 简介首次用户功能特点竞品和相关作品进一步探索 简介 bit&#xff0c;由 Chris Walz 于 2020 年使用 Go 语言开发&#xff0c;提供直观的命令行补全提示和建立在 git 命令之上的封装命令&#xff0c;旨在建立完全兼容 git 命令的现代化 CLI。 首次用户 使用 x bit 即可自…

【华为机试】2023年真题B卷(python)-矩阵元素的边界值

一、题目 题目描述&#xff1a; 给定一个N*M矩阵&#xff0c;请先找出M个该矩阵中每列元素的最大值&#xff0c;然后输出这M个值中的最小值。 补充说明: N和M的取值范围均为: [0,100] 二、示例 示例1&#xff1a; 输入: [[1,2],[3,4]] 输出: 3 说明: 第一列元素为: 1和3&…

Linux 进程(五) 调度与切换

概念准备 当一个进程放在cpu上运行时&#xff0c;是必须要把进程的代码跑完才会进行下一个进程吗&#xff1f;答案肯定是 不对。现在的操作系统都是基于时间片轮转执行的。 时间片&#xff08;timeslice&#xff09;又称为“量子&#xff08;quantum&#xff09;”或“处理器片…

求职招聘小程序平台运营版系统源码 全开源源代码 附带完整的安装与部署教程

近年来&#xff0c;移动互联网的普及&#xff0c;求职招聘行业也在逐步向数字化转型。在这个过程中&#xff0c;小程序因其便捷性、即时性等特点&#xff0c;成为了求职者和招聘方的新宠。罗峰来给大家分享一款求职招聘小程序平台运营版系统源码&#xff0c;致力于为用户提供高…

安装elasticsearch、kibana、IK分词器、扩展IK词典

安装elasticsearch、kibana、IK分词器、扩展IK词典 后面还会安装kibana&#xff0c;这个会提供可视化界面方面学习。 需要注意的是elasticsearch和kibana版本一定要一样&#xff01;&#xff01;&#xff01; 否则就像这样 elasticsearch 1、创建网络 因为我们还需要部署k…

Unable to connect to Redis server

报错内容&#xff1a; Exception in thread "main" org.redisson.client.RedisConnectionException: java.util.concurrent.ExecutionException: org.redisson.client.RedisConnectionException: Unable to connect to Redis server: 175.24.186.230/175.24.186.230…

Elasticsearch:带有自查询检索器的聊天机器人示例

本工作簿演示了 Elasticsearch 的自查询检索器 (self-query retriever) 将问题转换为结构化查询并将结构化查询应用于 Elasticsearch 索引的示例。 在开始之前&#xff0c;我们首先使用 langchain 将文档分割成块&#xff0c;然后使用 ElasticsearchStore.from_documents 创建…

多粒度在研究中的应用

FontDiffuser: One-Shot Font Generation via Denoising Diffusion with Multi-Scale Content Aggregation and Style Contrastive Learning 存在的问题 现有的字体生成方法虽然取得了令人满意的性能&#xff0c;但在处理复杂字和风格变化较大的字符(尤其是中文字符)时&#x…

lunux(mysql下载以及操作)

下载mysql 查看镜像 docker images 下载MySQL镜像 mysql/mysql-server:8.0 创建文件夹&#xff0c;创建配置文件和放数据文件 mkdir -p /data/mysql/{conf,,data} 创建配置文件 my.cnf 写入配置文件my.cnf的代码 [client] default-character-setutf8[mysql] de…

MySQL数据库高级SQL语句及存储过程

目录 一、高级SQL语句 &#xff08;一&#xff09;case语句 1.语法定义 2.示例 &#xff08;二&#xff09;空值(NULL) 和 无值( ) 1.区别 2.示例 &#xff08;1&#xff09;字符长度 &#xff08;2&#xff09;判断方法 ① 空值(NULL) ② 无值( ) &#xff08;3…

了解Apache 配置与应用

本章内容 理解 Apache 连接保持 掌握 Apache 的访问控制 掌握 Apache 日志管理的方法 Apache HTTP Server 之所以受到众多企业的青睐&#xff0c;得益于其代码开源、跨平台、功能 模块化、可灵活定制等诸多优点&#xff0c;不仅性能稳定&#xff0c;在安全性方面的表现也十分…

物联网-物联网概念初识

物联网&#xff1a;将无线通信技术、传感设备、全球定位系统或其他信息获取方式等各种传感器嵌入到各种物体、设施中。 物联网三层架构 感知层 ——> 传输层 ——> 应用层 一、物联网通信协议 LoRa技术&#xff1a;基于扩频技术的超远距离无线传输方案&#xff0c;Lo…

C++上位软件通过Snap7开源库访问西门子S7-200/合信M226ES数据块的方法

前言 上一篇文章中介绍了Snap7访问西门子S7-1200/S7-1500 DB块的方法&#xff0c;对于S7-200PLC是没有数据块访问的。S7-200PLC中Snap7只能通过访问MB块&#xff0c;VB块的方法进行和PLC之间的Snap7通信和数据交换。手头没有S7-200PLC故通过合信CTMC M226ES运动控制器进行测试&…

以爱之名,与“EYE”同行 蔡司光学公益行一直在路上

用心传递公益温暖之力&#xff0c;助力更多乡村学童拥有光明未来。2023年12月26日&#xff0c;一场以“EYE”为主题的公益活动正在中卫市宣和镇东台小学举办。本次&#xff0c;眼视光领域领军品牌蔡司光学携手中卫德明眼科的专业视光团队一同来到活动现场&#xff0c;为该校全体…

C++八股学习心得.2

1.C常量 常量是固定值&#xff0c;在程序执行期间不会改变。这些固定的值&#xff0c;又叫做字面量。 常量可以是任何的基本数据类型&#xff0c;可分为整型数字、浮点数字、字符、字符串和布尔值。 常量就像是常规的变量&#xff0c;只不过常量的值在定义后不能进行修改。 …

C# 语法进阶 委托

1.委托 委托是一个引用类型&#xff0c;其实他是一个类&#xff0c;保存方法的指针 &#xff08;指针&#xff1a;保存一个变量的地址&#xff09;他指向一个方法&#xff0c;当我们调用委托的时候这个方法就立即被执行 关键字&#xff1a;delegate 运行结果&#xff1a; 思…

第二证券:停牌意味着什么?

股票停牌的原因&#xff1f; 一般来说&#xff0c;股票停牌的原因可以分为以下几类&#xff1a; 1、上市公司有严峻情况变化&#xff0c;如企业并购、重组等&#xff0c;为了确保生意顺利完成和信息宣布的及时、充分、准确&#xff0c;避免商场出现信息不对称的情况&#xff…

喜报 | 群策群力,奋战半年 ! 钡铼技术顺利通过ISO9001质量管理体系认证

在这个中秋和国庆双节同庆的时刻之后&#xff0c;我想借此机会宣布一个好消息。钡铼技术已成功通过ISO 9001质量管理体系的认证啦。ISO 9001是全球范围内广泛应用的质量管理体系认证&#xff0c;具有极高的含金量和国际认可度。这一认证对公司的质量管理、环境保护和员工健康安…

软件工程期末总结

软件工程期末总结 软件危机出现的原因软件生命周期软件生命周期的概念生命周期的各个阶段 软件开发模型极限编程 可行性研究与项目开发计划需求分析结构化分析的方法结构化分析的图形工具软件设计的原则用户界面设计结构化软件设计面向对象面向对象建模 软件危机出现的原因 忽视…