前缀和与差分---概念+例题

news2025/1/31 3:04:01

目录

前缀和

概念

例题

差分

概念

例题


前缀和


概念

在高中学习了数列的概念,跟现在的数组很像。可以类比来看,数组的前缀和与数列的前n项和其实可以看成一个概念。很多算法题利用前缀和的思想也就是可以使用高中里面的前n项和来求解数组里面任何一个区间的元素的和。其公式为:

S{_n} = S{_{n - 1}} + a{_n}

结合实际例子来看:假设现在有数组arr:

arr = {1,2,3,4,5};

我们现在需要求数组里面下标区间为 [i , j] 的元素的大小Sum,计算公式为:

Sum = S{_j} - S{_{j - 1}}

注意:数组下标是从0开始计算的.

就具体步骤而言,前缀和的操作方法主要分为一下几步:

  • 预处理:先求出数组元素的总和S{_n} (n为数组长度)
  • 在循环遍历时维护一个数组a, 其中a[i] 表示下标从 0 到 i 的元素的和;
  • 在求具体的区间和的时候使用上面的结论即可.

算法的时间复杂度分析:        

前缀和算法的时间复杂度可以达到 O(n).

例题

1、链接:3956. 截断数组 - AcWing题库

 

思路如右图所示,不难得到解题步骤:

  1. 先遍历数组,预处理求出数组的总和s,如果s不是三的倍数,则表示无解;
  2. 从前往后枚举第二个点,判断此时数组的前缀和为 (3 / s) * 2:
    1. 是:则枚举第一个点,寻找前面数组前缀和为3/s的点的数量,即为cnt;
    2. 不是:则continue进入下一次循环
  3. 将每次得到的cnt求和,得到全部的方案数ans输出。结束.

本题的Java代码如下:

import java.util.Scanner;

import static java.lang.System.exit;

public class Acwing_3959 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] nums = new int[n + 5];
        int[] s = new int[n + 5];  // 表示数组的前n项和
        long ans = 0, sum = 0, cnt = 0;
   // ans表示总的方案数,sum表示数组的综合,cnt表示在当前元素之前有多少个第一个元素符合要求
        for (int i = 1; i <= n; i++) {
            nums[i] = scanner.nextInt();
            sum += nums[i];  // 计算数组的总和
            if(i == 1) s[i] = nums[i];
            else s[i] = nums[i] + s[i - 1];   // 计算数组的前n项和
        }
        if(sum % 3 != 0 || n < 3) {
            System.out.println(0);
            exit(0);
        }
        long average = sum / 3;
        for (int i = 1; i < n; i++) {
            if(s[i] == average * 2) ans += cnt; 
            if(s[i] == average) ++cnt;
        }
        System.out.println(ans);
    }
}

完成此题时有以下需要注意的技巧:

  • 前缀和数组最好从下标1开始,可以避免数组元素全为0不好计算子数组的数量的情况;

差分


概念

在前面了解了前缀和,和前缀和很像的一类算法还有差分。同样是对字符串做处理,前缀和可以在 o(1) 时间内求出数组某一段区间的元素的和,差分则可以在平均 o(n) 的时间内给数组某一段区间的元素加上(或减去)一个数。

  算法思路如下:

假设我们需要给数组下标为2到3的元素加上数字c , 则可以维护一个差分数组b, 开始时b的元素全部为0,将 b[2] 变为c, 同时将b[4] 变为 -c, 然后求差分数组的前缀和s[i], s[i]表示下标为 i 处的前缀和。最后将原数组的元素加上对应的差分数组的前缀和即可。不难证明,这种方法的时间复杂度为 o(n) , 而且跟增加操作的次数无关。也就是说,使用差分算法,不管对数组的区间增加多少次,算法的时间复杂度总是 o(n).

注意,在差分数组中求前缀和是很简单的操作。因为增加数组元素是一定要遍历原数组和差分数组的,只需要在增加原数组之前使用 S{_n} = S{_{n - 1}} + a{_n}求出前缀和即可,使用一个前缀和数组就可以在不影响算法整体时间复杂度的前提下完成求前缀和。

 

上面陈述的是实际解题中经常使用的一种思路,现在给出差分的定义:

定义:差分数组的每一个元素可以是原数组的当前元素与其前一个元素之差(i≠0),差分数组的第一个元素和原数组的第一个元素相等;

推论:差分数组的前缀和数组就是原数组。

证明:假设原数组为:

                        int arr = {2, 3, 4, 6, 7};

  不难求出差分数组为:

                        int b = {2, 1, 1, 2, 1};    将此差分数组求前缀和即可得到原数组。

不难证明对一般情况,此推论仍成立。

额外提醒:在实际操作中,我们需要将差分数组和元素组的下标从1开始计数,由于笔者水平限制,暂时还不能给出这样做的具体原理,但是大量的实践证明,这样做确确实实在无形中避免很多问题。

例题

题目链接:3729. 改变数组元素 - AcWing题库

 

图1 Acwing_3729(例题1截图)

解题思路:

根据差分的思想,可以将需要输出的数组看成原数组,在程序中维护一个差分数组。按照经验,差分数组和原数组的下标均应从1开始。采用“逻辑加”的概念来完成差分数组的前缀和与原数组的相加,即1 + 1 = 1, 将原数组的后面三个元素变成1的需求,可以转换实现为逻辑加1,原数组长度在变化,但是每一次都不难求出需要逻辑加1的元素的前后下标,那么这个题的整体需求就是跟原数组的指定区间逻辑加上1,操作有很多次。如果使用一般的方法,不难看出绝对会超出时间限制,因此采用差分的方法是本题的正解之一。

解题步骤如下:

  1. 准备一个全为0的差分数组,长度足够大;
  2. 同时遍历原数组和题设的整数数组,由于题设的整数数组只需要使用一次,因此可以取消数组的转存,将数组的输入和使用合在一起,用一个变量ret代替数组;
  3. 将得到的re转化为差分数组的下标区间(不难证明,下标区间为[i - ret + 1 , i +1]),随后分别给差分数组的这两个对应元素(k[i - ret + 1] 和 k[i + 1])加上1和-1。
  4. 维护差分数组的前缀和数组,将其和原数组对应相加即可。注意:将差分数组的前缀和与原数组相加的时候不可使用“逻辑加”,因为前缀和数组代表了增加1的重数,1表示增加了1个1,2表示增加了两个2,只有等全部的前缀和数组处理完成之后才能将大于1的数变成1,将小于1的数变成0;
  5. 将原数组逻辑处理后输出。结束.

本题的Java如下:

import java.util.Scanner;

public class Acwing_3729 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int t = scanner.nextInt();
        int[] k = new int[100005];   // 差分数组
        while(t-- > 0){
            int n = scanner.nextInt();
            int[] arr = new int[n + 5];
            for(int i = 1; i <= n; i++){
                int ret = scanner.nextInt();
                if(i - ret + 1 <= 1) k[1] += 1;
                else k[i - ret + 1] += 1;
                k[i + 1] += -1;
            }
            for (int i = 1; i <= n; i++) {
                arr[i] = arr[i - 1] + k[i];
                k[i] = 0;
                System.out.printf("%d ", arr[i] > 0 ? 1 : 0);
            }
            k[n + 1] = 0;
            System.out.println();
        }
    }
}

 PS:c站的富文本编辑器是真的难搞,这是我之前的博客,在word上已经写好了的,转到这个上面来都弄了半天。尤其是公式编辑器,真的难受。

@SoftwareTeacher 能优化以下吗~~~~~

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

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

相关文章

3年功能测试经验,面试想拿到15k很难吗?

一直觉得经验多&#xff0c;无论在哪都能找到满意的工作&#xff0c;但是现实却是给我打了一个大巴掌&#xff01;事后也不会给糖的那种... 个人情况 大概介绍一下个人情况&#xff0c;男&#xff0c;本科&#xff0c;三年多测试工作经验&#xff0c;一毕业因为不成熟的经验以…

大型数据库Oracle12C 复习笔记

大型数据库Oracle1 Oracle基础1.1 Oracle基础概念1.2 Oracle体系结构1.3 多租户架构1.4 Oracle进程1.5 内存结构1.6 文件系统2 Oracle查询3 Oracle对象4 Oracle编程1 Oracle基础 与MySQL、MSSQL不同&#xff0c;Oracle数据库系统是美国ORACLE&#xff08;甲骨文&#xff09;公…

还在用chatGPT聊天?《元宇宙2086》已开始用AIGC做漫画连载了!

ChatGPT 是由 OpenAI开发的一个人工智能聊天机器人程序&#xff0c;于 2022 年 11 月推出。该程序使用基于 GPT-3.5架构的大型语言模型并通过强化学习进行训练。 ChatGPT 目前仍以文字方式互动&#xff0c;而除了可以透过人类自然对话方式进行交互&#xff0c;还可以用于相对复…

多线程使用哈希表

❣️关注专栏: JavaEE 多线程环境使用哈希表&#xff0c;HashMap 本身不是线程安全的. 在多线程环境下使用哈希表可以使用: Hashtable&#xff08;不推荐使用&#xff09;ConcurrentHashMap&#xff08;推荐&#xff09; &#x1f388;1 Hashtable Hashtable是线程安全的&am…

OpenGL中图片尺寸和上屏尺寸不一致的变形问题解决

一、尺寸不一致问题&#xff1a; 图片加载到OpenGL纹理&#xff0c;再将纹理进行上屏的时候&#xff0c;可能会出现图片尺寸和屏幕尺寸不一致&#xff0c;导致图片绘制到屏幕时出现变形的问题&#xff1a; 二、根本原因&#xff1a; 原始图片的纹理尺寸&#xff08;Textur…

计网之IP协议和以太网

文章目录一. IP协议1. IPv4报头介绍2. 解决IPv4地址不够用的问题3. IP地址管理4. 路由选择二. 以太网三. 浅谈DNS域名解析系统一. IP协议 IP协议是位于OSI模型中第三层(网络层)的协议, 在这层上工作的不止这一个协议, 但IP协议是网络层传输所使用的最主流的一种协议, 有IPv4和…

C语言——指针进阶

目录 前言 一. 字符指针 二. 指针数组 三. 数组指针 3.1 何为数组指针&#xff1f; 3.2 &数组名与数组名 四. 数组参数、指针参数 4.1 一维数组传参 4.2 二维数组传参 4.3 一级指针传参 4.4 二级指针传参 五. 函数指针 六. 函数指针数组 七. 指向函数指针数组…

Java 继承中构造函数的问题

继承中构造函数的问题 父类构造函数和子类构造函数的关系 new一个之类对象时默认调用了父类的无参构造函数 ,创建了父类对象 父类的成员变量和成员方法才能创建出来 子类才能继承下来使用 首先创建一个父类&#xff0c;动物类 接下来创建一个子类&#xff0c;狗类&#xff…

基于OSG的虚拟校园系统的设计与实现

基于open scene graph的虚拟校园系统的设计与实现 摘要 •引言 • OSG基本原理 •OSG操作与动画 •视点的定位和切换 •自由漫游 •路径漫游 • 路径动画 • 点选和文字 • 粒子系统 • 3DMAX • 无线通信与数据库设计 • 实现步骤 • 结论 摘要 随着科技的不断发展,人工智能&a…

2023年,8种必备Selenium编写自动化用例的技巧

在开始自动化时&#xff0c;您可能会遇到各种可能包含在自动化代码中的方法&#xff0c;技术&#xff0c;框架和工具。有时&#xff0c;与提供更好的灵活性或解决问题的更好方法相比&#xff0c;这种多功能性导致代码更加复杂。在编写自动化代码时&#xff0c;重要的是我们能够…

2023年网络安全十大发展趋势

近日&#xff0c;中国计算机学会&#xff08;CCF&#xff09;计算机安全专委会中来自国家网络安全主管部门、高校、科研院所、大型央企、民营企业的委员投票评选出2023年网络安全十大发展趋势。 趋势一 数据安全治理成为数字经济的基石 我国《数据安全法》提出“建立健全数据…

spring的启动过程(一) :IOC容器的启动过程

一、web容器的加载 首先我们要先知道一个web项目的启动过程。 将Web项目部署到Tomcat中的方法之一&#xff0c;是部署没有封装到WAR文件中的Web项目。要使用这一方法部署未打包的webapp目录&#xff0c;只要把我们的项目&#xff08;编译好的发布项目&#xff0c;非开发项目&am…

掌握MySQL分库分表(五)SpringBoot2+MybatisPlus整合Sharding-Jdbc水平分表实现

文章目录创建Java-Maven项目创建数据库、表创建Java实体类配置启动类水平分表配置文件配置测试分库分表实现分析控制台SQL逻辑SQL真实SQL主键重复问题创建Java-Maven项目 添加依赖 <properties><java.version>11</java.version><maven.compiler.source&…

PMP考试有没有什么技巧可以介绍一下么?

一、试题形式 ——中英文对照 即每道题都是一遍英文&#xff0c;一遍翻译的中文&#xff0c;在审题的时候有一些小的技巧需要注意。首先如果你的英文水平足够好&#xff0c;建议直接阅读原文。PMP试题毕竟是美国人出的&#xff0c;语言的组织、思想的表达&#xff0c;肯定更符…

python居然能语音控制电脑壁纸切换,只需60行代码

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 家在日常的电脑使用中&#xff0c;都会有自己喜爱类型的桌面 单纯的桌面有时候会让人觉得单调 今天&#xff0c;就由我带领大家只用60行代码打造一款语音壁纸切换器程序&#xff0c; 让大家能够通过语音的方式来控制电脑去…

c++模板的简单认识

文章目录 前言一.泛型编程 函数模板 模板参数的匹配原则 类模板总结前言 ADD函数很好写&#xff0c;但是如果我们要有int类型的&#xff0c;double类型的&#xff0c;char类型的等等各种类型&#xff0c;难道要写这么多不同的ADD函数吗&#xff0c;这么写简直太麻…

Linux:基于bufferevent epoll tcp服务器代码

基于bufferevent epoll tcp服务器代码: #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <event2/event.h> #include <event2/buffer…

Excel/Word的一些快捷操作整理

Excel/Word的一些快捷操作整理 1. 给Word文件导入目录 前提&#xff1a;大致内容已经定稿&#xff0c;文章中各标题分级明确&#xff0c;最好各级标题字体大小设置不一样。 步骤&#xff1a;在头部导航栏&#xff0c;选择**“引用”–“目录”**&#xff0c;根据自己需求选择其…

KDJB1200六相继电保护测试仪

一、概述 KDJB1200继电保护测试仪是在参照电力部颁发的《微机型继电保护试验装置技术条件(讨论稿)》的基础上&#xff0c;广泛听取用户意见&#xff0c;总结目前国内同类产品优缺点&#xff0c;充分使用现代新的的微电子技术和器件实现的一种新型小型化微机继电保护测试仪。可…

DataWhale 大数据处理技术组队学习task3

四、分布式数据库HBase 1. 产生背景 1.1 Hadoop的局限性 优点&#xff1a;存储结构化、半结构甚至非结构化的数据&#xff0c;是传统数据库的补充&#xff0c;是海量数据存储的最佳方法。缺陷&#xff1a; 只能进行批处理&#xff0c;并且只能以顺序的方式访问数据。&#x…