【算法 C/C++】一维差分

2025 - 03 - 08 - 第 69 篇
Author: 郑龙浩 / 仟濹
【一维差分】

文章目录

  • 前缀和与差分 - 我的博客
  • 差分(一维)
    • 1 大体介绍
      • (1)**1 原数组 2 差分数组 3 差分数组的前缀和数组**
      • (2)记录区间操作的边界
    • 2 差分原理是什么???
    • 3 总结一下过程
    • 4 某些区间所有的元素 + 某数(不使用差分)
    • 5 某些区间所有的元素 + 某数(使用差分)

前缀和与差分 - 我的博客

一维前缀和
【算法 C/C++】一维前缀和
一维差分
【算法 C/C++】一维差分
二维前缀和
【算法 C/C++】二维前缀和
二维差分
【算法 C/C++】二维差分

差分(一维)

1 大体介绍

举例的数组

// 原数组
int arr[5] = {1, 3, 7, 5, 2};
// “原数组”的“差分数组” - 原生差分数组
int arr_d[5] = {1, 2, 4, -2, -3};
// 计算“差分数组”的“前缀和数组” --> 又变成了原数组
int sum_d[5] = {1, 3, 7, 5, 2};

(1)1 原数组 2 差分数组 3 差分数组的前缀和数组

// 原数组
1, 3, 7, 5, 2
// “原数组”的“差分数组” - 原生差分数组
1, 2, 4, -2, -3
// 计算“差分数组”的“前缀和数组” --> 又变成了原数组
1, 3, 7, 5, 2

差分数组

原生差分数组就是除了 arr_d[0] 外,其余 arrr_d[i] 都是 arr_d[ i ] = arr[i] - arr[i - 1]

说白了除了第一个元素是原封不动存入新的数组,其余的都是存储的后一个元素减去前一个元素的结果,重新存入一个新的数组

// “原数组”的“差分数组”
int arr_d[5] = {1, 2, 4, 2, -3};

原生差分数组 做前缀和计算 –> 原数组

当原生差分数组做前缀和计算的时候,会还原回原数组

// “原数组”的“差分数组” - 原生差分数组
int arr_d[5] = {1, 2, 4, 2, -3};
// 计算“差分数组”的“前缀和数组” --> 又变成了原数组
int sum_d[5] = {1, 3, 7, 5, 2};

(2)记录区间操作的边界

在增量差分数组中进行标记 –> 标记边界

当对 >=1 个区间进行加减value操作的时候,可以只计算 头 ** 和 ,然后进行差分计算前缀和计算**就可以得出加减的结果

( 无需再和以前一样通过循环进行累加操作了,节省了运算时间,提升了效率 )

Eg: [left, right] + value 的时候

操作如下

arr[left] += value;
arr[right + 1] -= value;

有多少个区间进行了 + value 操作,就循环多少次如上的操作

2 差分原理是什么???

这个原理想了挺久的,刚开始确实没明白,本来打算死记下来差分的步骤就好了,后来又看了一边网课,大致明白了,下面写一下

**一维差分 ** 的思路

define N 10
int arr_d[N] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

然后我想要对区间 [2, 6] 中所有的元素进行加 1 操作,则 原生差分数组 d[N] 的变化如下

  • arr[left] += value;
  • arr[right + 1] -= value;
// 下标:
 0  1  2  3  4  5  6  7  8  9
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
// 变化后 --> d[2] + 1 且 d[6 + 1] - 1
{0, 0, 1, 0, 0, 0, 0,-1, 0, 0}

前缀和操作

为什么是arr_d[2] + 1 且 arr_d[6 + 1] - 1呢,这个实际上就是确定了 要进行 +1 操作的区间,从 arr_d[2] 开始做前缀和计算,直到d[7]

过程:

d[2]开始做前缀和计算,答案一直为 1,直到加到d[6+1]的时候,此时d[7]-11 + (-1)答案为 0,也就是到这 +1操作截止了。

回过头去看过程,不就是利用差分数组确定要 +1 的区间吗。

结果为:

{0, 0, 1, 1, 1, 1, 1, 0, 0, 0}

那么进行了前缀和计算的差分数组是存储的什么呢???

实际上存储的就是所有元素要进行 +-value

所以,接下来的操作就是根据存储着所有位置要加减什么值得前缀和数组计算出不同区间+不同值得最终结果

增量差分数组!!!

注意区分,我上面的介绍均为 原生差分数组,因为课程中这么写的,我也就这样记得,后来写这个差分代码才意识到问题,又问了AI,才明白其实是并不是用的 这个原生差分数组,而是增量差分数组,然后增量差分数组刚开始并不是存储的每个位置的增量(增量差分数组在应用前缀和之前存储的是区间操作的边界标记。通过前缀和操作以后才是存储的每个位置的增量

操作区间标记方式意义
区间开始(left)delta[left] += value从位置left开始累积增加value
区间结束(right)delta[right+1] -= value在位置right+1取消value的累积

而在写程序的时候用 增量差分数组,直接存储每个元素位置的增量即可,而这个增量既可以是正也可以是负。

实际上在程序实现中,并不直接存储原数组的差分值,即不适用原生差分数组,而是通过 区间操作标记 来记录每个位置的增量变化(只存储增量),初始时 d 数组全为0,每个区间操作 [left, right] + value 会修改 d[left]d[right + 1],最终通过 前缀和计算 得到每个位置的总增量值。这些增量值会叠加到原数组上,完成最终的区间修改操作。

3 总结一下过程

假设现在所有的数值已经输入

//变量
arr[N] - 原数组
d[N] - 增量差分数组 - 刚开始存储的是区间操作的边界标记 - 前缀和操作后存储每个元素的增量
  1. 增量差分数组 d[N] 进行如下操作,每个区间操作对应一次修改。

    d[left] += value;
    if (right + 1 < N) d[right + 1] -= value; // 防止越界
    

    此过程用来计算 区间操作标记的位置

  2. 利用 d[N]进行前缀和计算,这时候计算得出来的才是 每个元素的增量

    for (int i = 1; i < N; i++) {
        d[i] += d[i - 1];  // 此时 d[i] 表示 arr[i] 的增量
    }
    
  3. 然后 将 d[N] 数组和 arr[N] 数组的所有元素进行一对一的相加操作,得出将增量值叠加到原数组的结果

    for (int i = 0; i < N; i++) {
        arr[i] += d[i];
    }
    

4 某些区间所有的元素 + 某数(不使用差分)

题目

有一个数组 arr[5] = {1, 3, 7, 5, 2},将如下区间内的元素加上指定的数字:

[2, 4] + 5, [1, 3] + 2, [0, 2] - 3,并打印更改后的数组

答案为:-2, 2, 11, 12, 7

思路

原来的方法就是,直接硬将这些区间内的数字±操作,当计算区间过多的时候,运算时间太长

比如计算 arr[2]的计算,就要进行三次运算 arr[2] + 5 + 2 - 3,最终得出11,如果有好多的话,太麻烦了

代码

// Author: 郑龙浩 / 仟濹
// 2025-03-06
// 有一个数组 `arr[5] = {1, 3, 7, 5, 2}`,将如下区间内的元素加上指定的数字:
// `[2, 4] + 5, [1, 3] + 2, [0, 2] - 3`,并打印更改后的数组
// 答案为:`-2, 2, 11, 12, 7 `
// 差分
#include <iostream>
#include <algorithm>
using namespace std;
#define N 5
// 方法2
// #define get_sum(a, b) (a ? sum[b] - sum[a - 1] : sum[b])
int arr[ N ] = {1, 3, 7, 5, 2}; // 原数组
int d[ N ] = { 0 }; // 全部置为0
// 利用“差分数组”存储需要改变的范围以及值
void arr_add(int left, int right, int value){
    d[ left ] += value;
    // 避免越界
    if( right + 1 < N) d[ right + 1] -= value; 
}
int main( void ){
    arr_add(2, 4, 5);
    arr_add(1, 3, 2);
    arr_add(0, 2, -3);
    // 对差分数组进行前缀和计算
    for( int i = 1; i < N; i ++ ){
        d[ i ] += d[i - 1];
    }
    // 将最终要加的值加到 arr[N] 原数组当中
    for( int i = 0; i < N; i ++ ){
        arr[ i ] += d[ i ];
    }
    // 打印结果
    for( int i = 0; i < N; i ++ ){
        cout << arr[ i ] << " ";
    }
    return 0;
}

5 某些区间所有的元素 + 某数(使用差分)

题目

有一个数组 arr[5] = {1, 3, 7, 5, 2},将如下区间内的元素加上指定的数字:

[2, 4] + 5, [1, 3] + 2, [0, 2] - 3,并打印更改后的数组

答案为:-2, 2, 11, 12, 7

思路

前面也写过了

//变量
arr[N] - 原数组
d[N] - 差分数组
  1. 原数组中的数据进行如下操作,有多少个区间进行 +value 操作就操作多少遍

    d[left] += value, d[right + 1] -= value;
    

    此过程用来计算差分数组中的值,而差分数组实际上就是存储的就是 对原数组的不同区间的加减不同的值为多少

  2. 利用 d[N]进行前缀和计算,计算出每个元素应该 ± 多少

  3. 然后 将 d[N] 数组和 arr[N] 数组的所有元素进行一对一的相加操作,得出最终答案。

代码

// Author: 郑龙浩 / 仟濹
// 2025-03-06
// 有一个数组 `arr[5] = {1, 3, 7, 5, 2}`,将如下区间内的元素加上指定的数字:
// `[2, 4] + 5, [1, 3] + 2, [0, 2] - 3`,并打印更改后的数组
// 答案为:`-2, 2, 11, 12, 7 `
// 差分
#include <iostream>
#include <algorithm>
using namespace std;
#define N 5
int arr[ N ] = {1, 3, 7, 5, 2}; // 原数组
int d[ N ] = { 0 }; // 全部置为0
// 利用“差分数组”存储需要改变的范围以及值
void arr_add(int left, int right, int value){
    d[ left ] += value;
    d[ right + 1] -= value; 
}
int main( void ){
    arr_add(2, 4, 5);
    arr_add(1, 3, 2);
    arr_add(0, 2, -3);
    // 对差分数组进行前缀和计算
    for( int i = 1; i < N; i ++ ){
        d[ i ] += d[i - 1];
    }
    // 将最终要加的值加到 arr[N] 原数组当中
    for( int i = 0; i < N; i ++ ){
        arr[ i ] += d[ i ];
    }
    // 打印结果
    for( int i = 0; i < N; i ++ ){
        cout << arr[ i ] << " ";
    }
    return 0;
}

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

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

相关文章

uniapp或者vue 使用serialport

参考https://blog.csdn.net/ykee126/article/details/90440499 版本是第一位&#xff1a;否则容易编译失败 node 版本 18.14.0 npm 版本 9.3.1 electron 版本 30.0.8 electron-rebuild 版本 3.2.9 serialport 版本 10.0.0 需要python环境 main.js // Modules to control app…

编程题-计算器(中等)

题目&#xff1a; 给定一个包含正整数、加()、减(-)、乘(*)、除(/)的算数表达式(括号除外)&#xff0c;计算其结果。 表达式仅包含非负整数&#xff0c;&#xff0c; - &#xff0c;*&#xff0c;/ 四种运算符和空格 。 整数除法仅保留整数部分。 解法一&#xff08;栈&…

数据增强术:如何利用大模型(LLMs)来模拟不同的扰动类型以增强信息提取任务的鲁棒性

一、对抗样本库构建 1. 基于LLMs的领域针对性扰动设计对抗样本生成 替换实体、三元组和触发器&#xff08;Replace Entity, Triple, and Trigger&#xff09; 使用LLMs&#xff08;如GPT-4&#xff09;来替换句子中的实体、关系三元组或事件触发器&#xff0c;同时保持其类型不…

深入了解Linux —— git三板斧

版本控制器git 为了我们方便管理不同版本的文件&#xff0c;就有了版本控制器&#xff1b; 所谓的版本控制器&#xff0c;就是能够了解到一个文件的历史记录&#xff08;修改记录&#xff09;&#xff1b;简单来说就是记录每一次的改动和版本迭代的一个管理系统&#xff0c;同…

Windows编程----进程的当前目录

进程的当前目录 Windows Api中有大量的函数在调用的时候&#xff0c;需要传递路径。比如创建文件&#xff0c;创建目录&#xff0c;删除目录&#xff0c;删除文件等等。拿创建文件的CreateFile函数做比喻&#xff0c;如果我们要创建的文件路径不是全路径&#xff0c;那么wind…

MyBatis-Plus分页控件使用及使用过程发现的一个坑

最近维护一个旧项目的时候&#xff0c;出现了一个BUG&#xff0c;经排查后发现是Mybatis-plus分页控件使用的时候需要注意的一个问题&#xff0c;故在本地使用MybatisPlus模拟出现了一下这个问题。 首先&#xff0c;先说一下MyBatis-Plus的使用&#xff1a; 1&#xff09;引入…

服务端和客户端通信(TCP)

服务端 using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading.Tasks;namespace TeachTcpServer {class Program{static void Main(string[] args){#region 知识点一 …

Windows下配置Flutter移动开发环境以及AndroidStudio安装和模拟机配置

截止 2025/3/9 &#xff0c;版本更新到了 3.29.1 &#xff0c;但是为了防止出现一些奇怪的bug&#xff0c;我安装的还是老一点的&#xff0c;3.19&#xff0c;其他版本的安装同理。AndroidStudio用的是 2024/3/1 版本。 — 1 环境变量&#xff08;Windows&#xff09; PUB_H…

C++11中的Condition_variable

C11中的condition_variable 在C11中&#xff0c;条件变量&#xff08;std::condition_variable&#xff09;是线程同步机制之一&#xff0c;用于在多线程环境中实现线程间的通信和协调。它允许一个或多个线程在某个条件尚未满足时等待&#xff0c;直到其他线程通知条件已经满足…

ROS2-话题学习

强烈推荐教程&#xff1a; 《ROS 2机器人开发从入门到实践》3.2.2订阅小说并合成语音_哔哩哔哩_bilibili 构建功能包 # create package demo_python_pkg ros2 pkg create --build-type ament_python --license Apache-2.0 demo_python_pkg 自己写的代码放在./demo_python_pkg/…

深度学习模型Transformer核心组件—前馈网络FFN

第一章&#xff1a;人工智能之不同数据类型及其特点梳理 第二章&#xff1a;自然语言处理(NLP)&#xff1a;文本向量化从文字到数字的原理 第三章&#xff1a;循环神经网络RNN&#xff1a;理解 RNN的工作机制与应用场景(附代码) 第四章&#xff1a;循环神经网络RNN、LSTM以及GR…

Helm安装chart包到k8s报错“不能重复使用名称,名称已被使用”

一、报错提示如下 “Error: INSTALLATION FAILED: cannot re-use a name that is still in use”,意思是安装chart时提供的名称已存在&#xff0c;不能重复使用同一个名称。 登录后复制 rootiZ:/usr/local/src/my-helm-repo/charts# helm install mymemcached3 memcached -n te…

容器编排革命:从 Docker Run 到 Docker Compose 的进化之路20250309

容器编排革命&#xff1a;从 Docker Run 到 Docker Compose 的进化之路 一、容器化部署的范式转变 在 Docker 生态系统的演进中&#xff0c;容器编排正从“手动操作”走向“自动化管理”。根据 Docker 官方 2023 年开发者调查报告&#xff0c;78% 的开发者已采用 Docker Compo…

对开源VLA sota π0的微调——如何基于各种开源数据集、以及你自己的私有数据集微调π0(含我司的微调实践)

前言 25年2.4日&#xff0c;几个月前推出π0的公司Physical Intelligence (π)宣布正式开源π0及π0-FAST&#xff0c;如之前所介绍的&#xff0c;他们对用超过 10,000 小时的机器人数据进行了预训练 该GitHub代码仓库「 π0及π0-FAST的GitHub地址&#xff1a;github.com/Ph…

VBA使用fso对象合并指定路径的txt文件(含子目录)

图(1) 前几天我跟大家分享了在VBA中如何获取指定类型文件的路径的方法&#xff0c;其中最重要的一个思路就是在处理完当前目录的文件后&#xff0c;再调用程序自身来对子目录进行处理&#xff0c;以此来实现对子目录的无限循环&#xff0c;直至所有文件都处理完毕为止。按照此设…

nginx反向代理功能

如上图所示&#xff0c;当配置好nginx反向代理服务器的时候&#xff0c;客户端向nginx反向代理服务器发送请求&#xff0c;nginx反向代理服务器再向真实服务器转发请求。 nginx作为反向代理就是利用nginx高并发&#xff0c;速度快的特性&#xff0c;让nginx能够承受更多的链接…

deepseek在pycharm中的配置和简单应用

对于最常用的调试python脚本开发环境pycharm&#xff0c;如何接入deepseek是我们窥探ai代码编写的第一步&#xff0c;熟悉起来总没坏处。 1、官网安装pycharm社区版&#xff08;免费&#xff09;&#xff0c;如果需要安装专业版&#xff0c;需要另外找破解码。 2、安装Ollama…

【江协科技STM32】ADC数模转换器-学习笔记

ADC简介 ADC&#xff08;Analog-Digital Converter&#xff09;模拟-数字转换器ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量&#xff0c;建立模拟电路到数字电路的桥梁&#xff0c;ADC是一种将连续的模拟信号转换为离散的数字信号的设备或模块12位逐次逼近型…

八卡5090服务器首发亮相!

AI 人工智能领域热度居高不下。OpenAI 的 GPT - 4 凭强悍语言处理能力&#xff0c;在内容创作、智能客服等领域广泛应用。清华大学团队的 DeepSeek 大模型在深度学习训练优势突出&#xff0c;正促使各行业应用端算力需求向推理主导转变&#xff0c;呈爆发式增长 。 随着 DeepS…

hadoop集群环境配置

目录 VMware虚拟机安装 Xshell安装 网络问题 centos7下载 ---------参考以下视频步骤进行生态搭建---------- 搭建好hadoop01 克隆出hadoop02、hadoop03 启动三台虚拟机 打开终端 输入 记录下各个ip 打开Xshell&#xff0c;新建会话 修改主机名 配置静态IP 主机名称…