【算法基础】差分数组详解

Smile, breathe and go slowly.

引入问题:

已知一个数组 a[ 10 ] ,初始值全部为1。 如果要将范围 [ 1, 5 ]之间的每一个数字都加1,应当如何操作。

最简单直接的操作就是for循环了:

for(int i=1;i<=5;i++) a[i]++;

但是如果数据范围较大,以及操作次数比较多,用for循环时间复杂度较高。

先看看什么是差分数组:

简单来说,前一个元素减去后一个元素形成的数组就是差分数组。

d[ 0 ] = a[ 0 ] ( i = 0 )

d[ i ] = a[ i ] - a[ i - 1 ] ( i > 0)

举个例子:

数组  a:
12345

差分数组 d 应当是:
11111     //第一个元素没有可以减的元素,所以就等于它本身

//即规定d[ 0 ] = a[ 0 ]

我们注意到:

d[ 1 ] = a[ 1 ] - a[ 0 ]

d[ 2 ] = a[ 2 ] - a[ 1 ]

d[ 3 ] = a[ 3 ] - a[ 2 ]

d[ 4 ] = a[ 4 ] - a[ 3 ]

即:

a[ 0 ] = d[ 0 ]

a[ 1 ] = d[ 1 ] + d[ 0 ]

a[ 2 ] = d[ 2 ] + d[ 1 ] + a[ 0 ]

可推导出 a[ i ] = d[ i ] + d[ i - 1 ] + d[ i - 2 ] + …+d[ 0 ]

所以差分数组到底有什么用呢?

回到引入问题

要使[ 1, 5 ] 之间的每个元素都+1,用差分数组只需要这样写:

d[ 1 ]++;
d[ 5+1 ]--;

因为d[ 1 ] = a[ 1 ] - a[ 0 ],a[ 1 ]增大了,a[ 0 ]不变,所以d[ 1 ]应当加上相应的变化量。
同理,中间的元素是同步增大的,所以无需处理。
对于d [ 6 ] = a[ 6 ] - a[ 5 ],a[ 5 ]增大了,a[ 6 ]不变,所以d[ 6 ]应当减去相应的变化量。

原本复杂的for循环,现在只需两行代码。

差分数组不仅仅是一个优秀的数据结构,还是一种很好的思想。
修改区间的时间复杂度是O(1),查询点的时间复杂度为O(n)

对应练习题:

基础题: CF44C Holidays(差分数组)

进阶题:P4939 Agent2(树状数组+差分数组)。

基础题代码 (Python版):

n, m = input().split()
n, m=int(n), int(m)
out=False
d=[0 for i in range(0,n+2)]   #d为差分数组
for i in range(0,m):
    a, b=input().split()
    a, b=int(a), int(b)
    d[a]+=1                #差分数组的更新
    d[b+1]-=1
for i in range(1,n+1):
    count=sum(d[0:i+1])   #求第i天浇水的次数
    if count!=1:         #浇水次数不等于1就输出,结束
        print(i,count)
        out=True
        break
if not out:        #没有问题就输出OK
    print("OK")

进阶题代码(C++版):
(这题要用树状数组来维护更新,关于树状数组,大家可以自己在网上查阅相关资料,或者在评论区讨论)

#include<iostream>
using namespace std;
int d[10000002];
int n, m;
int lowbit(int x) {
	return x & -x;
}
int getSum(int a) {
	int sum = 0;
	for (int i = a; i >= 1; i -= lowbit(i)) {
		sum += d[i];
	}
	return sum;
}
void update(int a, int b) {
	for (int i = a; i <= n; i += lowbit(i)) {
		d[i]++;
	}
	for (int i = b+1 ; i <= n; i += lowbit(i)) {
		d[i]--;
	}
}
int main() {
	ios::sync_with_stdio(false);
	cin >> n >> m;
	int a, b, c;
	for (int i = 1; i <= m; i++) {
		cin >> c;
		if (c) {
			cin >> a;
			cout << getSum(a) << endl;
		}
		else{
			cin >> a >> b;
			update(a, b);
		}
	}
	return 0;
}
发布了17 篇原创文章 · 获赞 24 · 访问量 1923

猜你喜欢

转载自blog.csdn.net/qq_45271256/article/details/103317447