用树状数组解决逆序对问题(以一本通1311题为例)
一、关于树状数组
树状数组具体的原理在此处不提,如果有想要了解的可以点击以下视频链接(强烈推荐,讲的十分清楚)
〔manim | 算法 | 数据结构〕 完全理解并深入应用树状数组
1.int lowbit(int x):返回数x二进制从后向前第一个为1的数(例如7的二进制表达为0b0111,函数的返回2的0次方1;8的二进制表达为0b1000,返回2的3次方8)。
2.void (int index):将位置为index的数加一。
3.int ask(int index):返回该数组index以及之前的数之和。
下面是树状数组具体实现代码(至于为什么要将数组初始化为0涉及到之后的算法):
int lowbit(int x) {
return x & (-1 * x);
}
class tree_list {
public:
void add(int index) {
for (; index <100005; index += lowbit(index)) {
arry1[index]++;
}
}
int ask(int index) {
int ans = 0;
for (; index ; index -= lowbit(index)) {
ans += arry1[index];
}
return ans;
}
private:
long long int arry1[100015] = {0};
};
二、求逆序数
先列举一个数组 arry[5]={5,4,3,2,1}
用传统的方法向后枚举可以和每个数组成逆序对的数。这样一个长度为n的数组的第i个数就要枚举i*n-i次,而这样做的时间复杂度为o(n^2)。这样处理的每次数据无法对下一次处理的数据做出贡献,所以我们会浪费很多时间,用下面的方法可以将前一次处理的结果为下一次用,可以将时间复杂度缩减为o(nlog(n))
算法原理:
我们从后面向前扫描并且开一个数组用来arry[i]来储存此时i出现的次数,还是以arry[5]={5,4,3,2,1}为例:
初始时
a[6]={0,0,0,0,0,0}
注意,arry储存的为a[i]大小的数出现的次数,所以arry的大小应该为a中最大的数+1
第一次扫描:
arry[5]=1,我们在arry[a[5]]的位置加一,
此时
a=[0,1,0,0,0,0]
在a[arry[5]]之前的数之和为arry[5]=1的逆序数,为0
第二次扫描
arry[4]=2
此时
a=[0,1,1,0,0,0]
a[arry[4]]前数的和为arry[4]=2的逆序数,为1
也就是说在i时刻a[i]的逆序数为arry[a[i]]之前的所有数之和
根据树状数组线上求和的特点,便可以解决该问题。
代码如下(还是以该题为例):
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> arry[i];
}
tree_list solution_list;//创建应该树状数组对象
long long int ans = 0;
for (int i = n - 1; i >= 0; i--) {
solution_list.add(arry[i]);//记录i时arry【i】出现的次数
ans+=solution_list.ask(arry[i]-1);//查看arry【i】前比他小的数的和并且加到最后的答案中去
}
cout << ans;
}
AC代码:
#include<iostream>
#include<vector>
using namespace std;
int arry[100015];
int lowbit(int x) {
return x & (-1 * x);
}
class tree_list {
public:
void add(int index) {
for (; index <100005; index += lowbit(index)) {
arry1[index]++;
}
}
int ask(int index) {
int ans = 0;
for (; index ; index -= lowbit(index)) {
ans += arry1[index];
}
return ans;
}
private:
long long int arry1[100015] = {0};
};
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> arry[i];
}
tree_list solution_list;
long long int ans = 0;
for (int i = n - 1; i >= 0; i--) {
solution_list.add(arry[i]);
ans+=solution_list.ask(arry[i]-1);
}
cout << ans;
}
该题链接一本通[1311]
该题在力扣对应题目剑指offer.51
如有错误欢迎纠正