下文为了方便起见,
1/ 单点(single point 简写 s)
2/区间 (distance 简写 d)
3/更新 (update 简写 u)
4/查询(require 简写 r)
比如单点更新,单点查询,我们用SUSR
单点更新,单点查询(SUSR)
传统数组就可以做,不说了
单点更新,区间查询(SUDR)
这个用线段树和树状数组都能完成,线段树的模板,已经在我另一篇博客里,下面献上树状数组的模板,关于树状数组的原理分析自行了解。
class TreeArray_for_SUDR: # when using, index start from 0
def __init__(self, nums: List[int]):
self.n = len(nums)
self.arr = [0] * (self.n + 1)
for i in range(self.n):
self.update(i, nums[i])
def __lowbit(self, i) -> int:
return i & (-i)
def update(self, i, k) -> int: # add k in index_i
i += 1
while (i <= self.n):
self.arr[i] += k
i += self.__lowbit(i)
def getSum(self, i) -> int: # get the sum from 0 to i
i += 1
res = 0
while (i > 0):
res += self.arr[i]
i -= self.__lowbit(i)
return res
区间更新,单点查询(DUSR)
如果题目是让你把x~y区间内的所有值全部加上k或者减去k,然后查询操作是问某个点的值,这种时候该怎么做呢。如果是像上面的树状数组来说,就必须把x-y区间内每个值都更新,这样的复杂度肯定是不行的,这个时候,就不能再用数据的值建树了,这里我们引入差分,利用差分建树。
假设我们规定A[0] = 0;
则有 A[i] = Σij = 1D[j];(D[j] = A[j] - A[j-1]),即前面i项的差值和,这个有什么用呢?例如对于下面这个数组
A[] = 1 2 3 5 6 9
D[] = 1 1 1 2 1 3
如果我们把[2,5]区间内值加上2,则变成了
A[] = 1 4 5 7 8 9
D[] = 1 3 1 2 1 1
发现了没有,当某个区间[x,y]值改变了,区间内的差值是不变的,只有D[x]和D[y+1]的值发生改变,至于为什么我想我就不用解释了吧。
所以我们就可以利用这个性质对D[]数组建立树状数组
代码模板为:
#ConstantPalindromeSum => CPS, 指区间查询
class TreeArray_CPS_for_DUSR:
def __init__(self, nums: List[int]):
self.n = len(nums)
self.arr = [0] * (self.n + 2)
for i in range(self.n):
self.__updateNum(i, nums[i]-nums[i-1] if i!=0 else nums[i])
print(self.arr)
def __lowbit(self, i) -> int:
return i & (-i)
def updateDistance(self, from_i: int, to_i: int, k: int) -> None: #add k from from_i to to_i
self.__updateNum(from_i, k)
self.__updateNum(to_i+1, -k)
def __updateNum(self, i, k) -> int: # add k in index_i
i += 1
while (i <= self.n):
self.arr[i] += k
i += self.__lowbit(i)
def getNum(self, i) -> int: # get in i
i += 1
res = 0
while i > 0:
res += self.arr[i]
i -= self.__lowbit(i)
return res
区间更新,区间查询(DUDR)
上面我们说的差值建树状数组,得到的是某个点的值,那如果我既要区间更新,又要区间查询怎么办。这里我们还是利用差分,由上面可知
∑ni = 1A[i] = ∑ni = 1 ∑ij = 1D[j];
则A[1]+A[2]+…+A[n]
= (D[1]) + (D[1]+D[2]) + … + (D[1]+D[2]+…+D[n])
= nD[1] + (n-1)D[2] +… +D[n]
= n * (D[1]+D[2]+…+D[n]) - (0D[1]+1D[2]+…+(n-1)D[n])
所以上式可以变为∑ni = 1A[i] = n∑ni = 1D[i] - ∑ni = 1( D[i](i-1) );
如果你理解前面的都比较轻松的话,这里也就知道要干嘛了,维护两个数状数组,sum1[i] = D[i],sum2[i] = D[i](i-1);
附上自己整理的代码模板:
class TreeArray_CPS_for_DUDR1: # when using, index start from 0
def __init__(self, nums: List[int]):
self.n = len(nums)
self.arr1 = [0] * (self.n + 1)
self.arr2 = [0] * (self.n + 1)
for i in range(self.n):
self.updateFromStart(i, nums[i] - nums[i - 1] if i != 0 else nums[i])
def __lowbit(self, i: int) -> int:
return i & (-i)
def updateFromStart(self, i: int, k: int) -> int: # add k to index_i
i += 1
x = i
while (i <= self.n):
self.arr1[i] += k
self.arr2[i] += k * (x - 1)
i += self.__lowbit(i)
def updateDistancePoints(self, from_i: int, to_i: int, k: int) -> None: # add k from from_i to to_i
self.updateFromStart(from_i, k)
self.updateFromStart(to_i + 1, -k)
def getSumInDistance(self, from_i: int, to_i: int) -> int: # get the sum from from_i to to_i
return self.getSumFromStart(to_i) - (self.getSumFromStart(from_i - 1) if from_i != 0 else 0)
def getSumFromStart(self, i: int) -> int: # get the sum from 0 to i
i += 1
res, x = 0, i
while (i > 0):
res += x * self.arr1[i] - self.arr2[i]
i -= self.__lowbit(i)
return res
更新版本,来自力扣的某位dalao,因为写得实在太棒了!!!先不问自取,若dalao看到请联系,我在做进一步的处理!!
class TreeArray_CPS_for_DUDR:
def __init__(self, n):
self.n = n
self.t1 = [0] * (n + 1)
self.t2 = [0] * (n + 1)
def add(self, t, x, k):
while x <= self.n:
t[x] += k
x += (x & (-x))
def query(self, t, x):
c = 0
while x:
c += t[x]
x -= (x & (-x))
return c
def add_range(self, begin, end, k):
self.add(self.t1, begin, k)
self.add(self.t1, end, -k)
self.add(self.t2, begin, (begin - 1) * k)
self.add(self.t2, end, - (end - 1) * k)
def query_sum(self, x):
return self.query(self.t1, x) * x - self.query(self.t2, x)
def query_range(self, begin, end):
return self.query_sum(end - 1) - self.query_sum(begin - 1)
测试:
数据准备,随机生成一个5000大小的数组,然后随机生成10000次操作,如下图所示:
import random as r
length = 5000
arr = [0] * length
for i in range(length):
arr[i] = r.randint(1, 50000)
oper = []
time = 100000
for i in range(time):
from_i, to_i = 95, 1
while from_i > to_i:
from_i = r.randint(0, length - 1)
to_i = r.randint(0, length - 1)
oper.append([from_i, to_i])
tcf = TreeArray_CPS_for_DUDR(arr)
测试代码:
import time as t
# 计算时间消耗,特别是对程序运行时间消耗
start = t.time()
tcf = TreeArray_CPS_for_DUDR(arr)
for _ in range(time):
tcf.updateDistancePoints(oper[_][0], oper[_][1], _)
tcf.getSumInDistance(oper[_][0], oper[_][1])
end = t.time()
print("树状数组+差分数组循环运时间:%.2f秒" % (end - start))
# 计算时间消耗,特别是对程序运行时间消耗
b = Baoli(arr)
start = t.time()
for _ in range(time):
b.updateDistancePoints(oper[_][0], oper[_][1], _)
b.getSumInDistance(oper[_][0], oper[_][1])
end = t.time()
print("暴力循环运行时间:%.2f秒" % (end - start))
最后结果:
树状数组+差分数组循环运时间:1.41秒
暴力循环运行时间:16.40秒
线段树求区间最大值(模板)
class SegTree {
public:
vector<int> arr;
int n;
SegTree(int n){
this->n = n;
this->arr = vector<int>(4*n, INT_MIN);
}
void update(int index, int val){
update(1, 1, n, index, val);
}
void update(int id, int l, int r, int index, int val){
if(l>r || index<l || r<index) return;
if(l<=index && index<=r)
arr[id] = max(arr[id], val);
if(l==r) return;
int mid = (l+r) / 2;
update(id<<1, l, mid, index, val);
update((id<<1)+1, mid+1, r, index, val);
}
int query(int l, int r){
return query(1, 1, n, l, r);
}
int query(int id, int l, int r, int ql, int qr){
if(r<ql || qr<l) return INT_MIN;
if(l==r || ql<=l && r<=qr) return arr[id];
int mid = (l+r) / 2;
int l_max = query(id<<1, l, mid, ql, qr);
int r_max = query((id<<1)+1, mid+1, r, ql, qr);
return max(l_max, r_max);
}
};
引用:
https://www.cnblogs.com/xenny/p/9739600.html