C++差分数组

前言

C++算法与数据结构
打开打包代码的方法兼述单元测试

差分数组(Difference Array)是一种巧妙的数据结构,常用于处理序列的各种动态修改问题,尤其是具有前后元素关联的操作。令数据数组是data,diff[i]记录data[i]-data[i-1],则data[i] = diff[0…i]之和。差分数组的优点:常数时间进行区间修改。

引言

张三有a个桃,李四比张三多d1个桃,王五比李四多d2个桃… 求大家各有多少个桃。
令数组 diff = {a,d1,d2…}
数组a记录张三、李四、王五拥有的桃子数。则:
a[0] = diff[0]
a[1] = diff[0]+diff[1]
a[2] = diff[0] + diff[1] + diff[2]
即 a[i] = sum(diff[0…i]) ,即前缀和。
本题的diff就是差分数组。
在这里插入图片描述

典型应用

令共有n个顾客,张三的编号为0,李四的编号为1,王五的编号为2…
经过m次操作(编号i1到i2都顾客都给予x个桃)后,求各顾客拥有的桃子数。
每次操作,区间修改差分数组,时间复杂度:O(1)。m次操作时间复杂度O(m)。
操作结束后,统一计算前缀和,时间复杂度O(n)。
解题时间复杂度:O(n+m),暴力解题的时间复杂度是:O(nm)

差分数组

令 a[i] = ∑ j : 0 i v D i f f [ i ] \sum_{j:0}^{i}vDiff[i] j:0ivDiff[i]
如果 vDiff[i1]++,则a[i1…]全部++
如果vDiff[i2]–,则a[i2…]全部–。
令11 < i2 ,则:
{ a [ i ] 不变,不受加减影响 i < i 1 a [ i ] 不变,加减抵消 i > = i 2 a [ i ] + + o t h e r \begin{cases} a[i]不变,不受加减影响 && i < i1 \\ a[i]不变,加减抵消 && i >= i2\\ a[i]++ && other \\ \end{cases} a[i]不变,不受加减影响a[i]不变,加减抵消a[i]++i<i1i>=i2other
即:a[i1…i2-1]++ ,其它不变。
区间更新、单点更新时间复杂度:O(1)。
区间查询、单点查询:O(n)
依次查询时间复杂度O(n),i从0到n-1查询a[i]的总时间复杂度是O(n)。
可与树状数组结合:更新查询全部是O(logn)
空间复杂度:O(n)
为了处理边界情况,vDiff往往比a多一个元素,方便处理最后一个元素+1。vDiff[n-1]++ vDiff[n]–。

题解

部分文章已完成,将陆续发布,估计2024年10月11号之前发布完毕。

难度分
2406. 将区间分为最少组数1731
2381. 字母移位 II1793
【滑动窗口】【差分数组】C++算法:995K 连续位的最小翻转次数1835
1589. 所有排列中的最大和1871
1526. 形成目标数组的子数组最少增加次数1872
1943. 描述绘画结果1969
【区间合并 差分数组】2963:统计好分割方案的数目1984
3224. 使差值相等的最少数组改动次数1996
2772. 使数组中的所有元素都等于零2029
二分查找 差分数组LeetCode2251:花期内花的数目2022
【C++差分数组】3229. 使数组等于目标数组所需的最少操作次数2066
【分解质因数 差分数组】2584. 分割数组使乘积互质2159
【差分数组】1674. 使数组互补的最少操作次数2333
【前缀和 二分查找 差分数组】2528最大化城市的最小供电站数目2235
【差分数组】【图论】【分类讨论】【整除以2】3017按距离统计房屋对数目2709
【上下界分析 差分数组】798得分最高的最小轮调

用map实现的差分

封装类

template<class KEY=int,class VALUE=int>
class CMapDiff
{
public:
	void Set(KEY left, KEY rExclue, VALUE value) {
		m_mDiff[left]+= value;
		m_mDiff[rExclue]-= value;
	}
	vector<pair<KEY, VALUE>> Ans()const {
		vector<pair<KEY, VALUE>> res;
		VALUE sum = 0;
		for (const auto& [key,value]: m_mDiff) {			
			sum += value;
			res.emplace_back(make_pair(key, sum));
		}
		return res;
	}
protected:
	map<KEY, VALUE> m_mDiff;
};

【区间合并 差分 栈】3169. 无需开会的工作日
大约2024年7月3号发

mDiff[si]++ mDiff[ei+1]-- 表示[si,ei] 一场会议。
∀ \forall mDiff的键 key,其下一个键为nkey。
∀ \forall k ∈ \in [key,nkey) mDiff[k]都为0,省略。
即:
x = ∑ i : 0 k e y m D i f f [ i ] = ∑ i : 0 k m D i f f [ i ] x = \sum_{i:0}^{key}mDiff[i] \quad = \sum_{i:0}^{k}mDiff[i] x=i:0keymDiff[i]=i:0kmDiff[i]
如果x不为0,则[key,nkey)全部要开会。

二维差分

a[i][j] = ∑ i 1 : 0 i ∑ j 1 : 0 j v D i f f [ i ] [ j ] \sum_{i1:0}^i \sum_{j1:0}^jvDiff[i][j] i1:0ij1:0jvDiff[i][j] 即以(0,0)为左上角,(i,j)为右下角的长方形之和。
a[i1…i2][j1…j2] ++的操作:
vDiff[i1][j1]++ vDiff[i2+1][j2+1]++
vDiif[i1][j2+1]-- vDiff[2+1][j1]–
注意:差分都是左闭右开空间
求前缀和的简单方法:
vCol[j] = ∑ i 1 : 0 i v D i i f [ i 1 ] [ j ] \sum_{i1:0}^{i}vDiif[i1][j] i1:0ivDiif[i1][j]
a[i][j] = ∑ j 1 : 0 j v C o l [ j 1 ] \sum_{j1:0}^j vCol[j1] j1:0jvCol[j1]

在这里插入图片描述
在这里插入图片描述

以(0,0)为左上角,(r,c)为右下角的长方形之和,分以下五种情况:
一, 包括(r2+1,c2+1)的长方形,和为1-1+1-1=0,上图绿色显示。
二, 排除情况一,包括(r1,c2+1)的长方形,必定包括(r,c),且不包括其它点,其和为1-1=0。上图蓝色显示。
三, 包括(r2+1,c1)的长方形和情况二类似。
四, 不包括四个单格的矩形,其和为0。上图红色显示。
五, 余下的单格,只包括(r1,c1),和为1。刚好是(r1,c1)为左上角,(r2,c2)位为右下角的长方形。

封装类

template<class T = int >
class CDiff2
{
public:
	CDiff2(int r, int c):m_iR(r),m_iC(c) {
		m_vDiff.assign(m_iR, vector<T>(m_iC));
	}
	void Set(int r1, int c1, int r2Exinc, int c2Exinc,int iAdd) {
		m_vDiff[r1][c1] += iAdd;
		m_vDiff[r2Exinc][c2Exinc] += iAdd;
		m_vDiff[r1][c2Exinc] -= iAdd;
		m_vDiff[r2Exinc][c1] -= iAdd;
	}
	vector<vector<T>> Ans()const {
		vector<vector<T>> res(m_iR, vector<T>(m_iC));
		vector<T> vCols(m_iC);
		for (int r = 0; r < m_iR; r++) {
			T iSum = 0;
			for (int c = 0; c < m_iC; c++) {
				vCols[c] += m_vDiff[r][c];
				iSum += vCols[c];
				res[r][c] = iSum;
			}
		}
		return res;
	}
	
	const int m_iR, m_iC;
protected:
	vector<vector<T>> m_vDiff;
};

题解

难度分
【离散化 二维差分】850. 矩形面积 II2335
【二维差分】2132. 用邮票贴满网格图2364
【离散化 二维差分】391. 完美矩形

数组数组实现差分

区间修改:O(logn)
查询:O(logn)

扩展阅读

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关推荐

我想对大家说的话
喜缺全书算法册》以原理、正确性证明、总结为主。
按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

评论 145
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闻缺陷则喜何志丹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值