Home Page

简介

今天在做leetcode每日一题的时候,又学到了一个特殊的数组《差分数组》。链接

差分数组的求取

对于一个正常的数组A,对于一个新建的数组B用来存储A的差分数组,那么B[i] = A[i]-A[i-1];
差分数组还有一个重要的特性:
对于差分数组B,B的前缀和就是原数组A。

用处

现在有一个数组C,想要在C上做以下操作:

  1. [1,5]区间内的值同时增加10;
  2. [2,10]区间内的值同时增加100;

当然可以使用循环遍历先让[1,5]区间内的数字增加10,然后再循环遍历让[2,10]同时增加100。在数据量比较小的时候这么做可行,如果数据量一大,需要做的操作一多,就会导致花费大量时间。

有没有办法可以在只遍历两次数组C,就可以完成操作呢?那就是使用差分数组:

  1. 先对C求取差分数组D。
  2. 想象一下,如果想要对整个C数组内的所有数字加10应该怎么做?可以在D[0]位置加10,然后对D做前缀和,那么得到的数组就是C中所有数字加10。
  3. 如果只想要某个区间内[i,j]的数字加10呢,可以在D[i]位置加10,然后在D[j+1]的位置再减10,这样的话得到的前缀数组其他的位置相对于C是不变的,但[i,j]区间内的数字相对于C来说都会增加10。
  4. 在需要操作多个区间的时候,差分数组可以有效的减少时间占用。

实例

struct IntervalAdds
{
	int m_start;
	int m_end;
	int m_add;
	IntervalAdds(int start, int end, int add) :m_start(start), m_end(end), m_add(add) {}
};
class Solution 
{
public:
	vector<int>IntervalAdd(const vector<IntervalAdds>& intervalAdds, const vector<int>& initialNums)
	{
		if (initialNums.size() <= 0)return{};
		vector<int>Difference(initialNums.size(), 0);

		//获取差分数组
		Difference[0] = initialNums[0];
		for (auto i = 1; i < initialNums.size(); ++i)
		{
			Difference[i] = initialNums[i] - initialNums[i - 1];
		}

		//区间加
		for (const auto& iter : intervalAdds)
		{
			if (iter.m_start > iter.m_end)continue;
			if (iter.m_start < Difference.size())
			{
				Difference[iter.m_start] += iter.m_add;
			}
			if (iter.m_end + 1 < Difference.size())
			{
				Difference[iter.m_end+1] -= iter.m_add;
			}
		}

		//前缀和
		for (auto i = 1; i < Difference.size(); ++i)
		{
			Difference[i] = Difference[i] + Difference[i - 1];
		}

		return Difference;
	}
};

Solution sl;
vector<IntervalAdds> adds{ {0, 2, 10},{3, 10, 20}, {5, 9, 8} };
vector<int>sorce(20, 0);
auto res = sl.IntervalAdd(adds, sorce);
for (auto i : res)cout << i << " ";

输出:image.png

Q.E.D.