树状数组
树状数组也是高级数据结构之一
树状数组,利用数的二进制特征进行检索的一种树状结构
是一种真正的高级数据结构:二分思想(O(logn)))、二叉树、位运算、前缀和
树状数组的基本应用:
-
修改元素add(k,x): 把a[k]加上x。
-
求和:
sum(x) = a1+ … +ax
区间和ai+…+aj=sum(j)-sum(i-1)
求区间和(只查询)
代码:
A = [0,4,5,6,7,8,9,10,11,12,13]
sum = [0]*20
sum[1] = a[1]
for i in range(2,11): #计算前缀和
sum[i]=a[i]+sum[i-1]
print(sum)
for i in range(1,11): #用前缀和反推计算数组a[]:
print(sum[i]-sum[i-1],end=' ')
print("[5,8]=",sum[8]-sum[4]) #查询区间和,例如查询[5,8]
那如果数列是动态的呢?
修改元素效率不发生变化
但对于区间和: sum(i)-sum(i-1) 复杂度: O(n)
效率很低
那么就可以引入树状数组的概念
定义
动态修改、求区间和:用树状数组
先展示代码:
def lowbit(x):
return x&-x
def add(x, d):
while(x<n):
tree[x] += d
x += lowbit(x)
def sum(x):
ans = 0
whlie(x>0):
ans += tree[x]
x -= lowbit(x)
return ans
树状数组的结构与二叉树相似
右边的每个点的值等于其子树的值的和
代码中的lowbit的功能:找到x的二进制数的最后一个1
从lowbit推出tree[]数组,所有的计算都基于tree[]
另m=lowbit(x)
定义tree[x]:把a[x]和他前面共m个数 相加
例:lowbit(6)=2,有tree[6] = a[5]+a[6]
用图来表示:
有了tree[],就可以进行基于tree[]的计算
基于tree[]的计算
求和
那以上关系是如何得到的?借助lowbit(x)
数组更新
树状数组处理逆序对问题
对于逆序对问题(对数组中没两个元素,前边的大于后边的就代表是一对逆序对)
题目样式:
老样子,先暴力:
n = int(input())
a = list(map(int,input().split()))
res=0
for i in range(n):
for j in range(i+1,n):
if a[j]<a[i]:
res +=1
print(res)
模拟:先检查第一个数a1,把后面所有数跟它比较,如果发现有一个比a1小,就是一个逆序对;再检查第二个数,第三个数…;直到最后一个数
复杂度:O(N2)
经观察,暴力法的执行过程和交换排序很想,可以联想到归并排序,是否可以处理?
def merge(L, mid, R):
global res
i = L; j = mid+1; t=0
while(i <= mid and j <= R):
if(a[i] > a[j]):
b[t] = a[j]; t+=1; j+=1
res = res + mid-i+1 #记录逆序对数量
else: b[t] = a[i]; t+=1 i+=1
#一个子序列中的数都处理完了,另一个还没有,把剩下的复制过来
while i <= mid: b[t]=a[i]; t+=1; i+=1
while j <= R: b[t]=a[j]; t+=1; j+=1
for i in range(t): a[L+i] = b[i]#把排好序的b[]复制回a[]
def merge_sort(L, R):
if L<R:
mid = (L + R) // 2 #平分成两个子序列
merge_sort(L, mid)
merge_sort(mid+1, R)
merge(L, mid, R)
n = int(input())
a = list(map(int,input().split()))
b = [0]*n
res = 0
merge_sort(0, n-1)
print(res)
相比普通的归并排序,这里只多了一个res = res + mid - i +1 用于记录逆序对数量
以上两种是基础思想,用树状数组解决逆序对问题是树状数组的巧妙应用
倒序:
正序:
n = int(input())
a = list(map(int,input().split()))
b = sorted(a)
for i in range(n): a[a.index(b[i])] = i+1
tree = [0] * (n+1)
def lowbit(x):
return x & -x
def update(x, d):
while(x < n):
tree[x] += d
x += lowbit(x)
def sum(x):
ans = 0
while(x > 0):
ans += tree[x]
x -= lowbit(x)
return ans
res = 0
a.insert(0,0) #在最前面加个0
for i in range(len(a)-1, 0, -1):
update(a[i], 1)
res += sum(a[i]-1)
print(res)