求逆序对的三种方法
今天上线代提了句逆序数,并布置了一个编程求逆序数的思考题,正好借此机会总结一下求逆序对的三种方法:暴力、归并排序以及树状数组
暴力
第一种暴力:写两层暴力比较,时间复杂度O(n^2)
for (int i = 1; i < n; i++)
for (int j = i + 1; j <= n; j++)
if (a[i] > a[j]) cnt++;
但是这种解法虽然容易写,但这爆炸的时间复杂度显然是不够完美的,于是便有了接下来的两种方法
归并排序
众所周知,归并排序是将数列 a[l,h] 分成两半:a[l,mid] 和 a[mid+1,h] 分别进行归并排序,然后再将这两半合并起来。在合并的过程中(设 l<=i<=mid,mid+1<=j<=h),当 a[i]<=a[j] 时,并不产生逆序数;当a[i]>a[j]时,在前半部分中比a[i]大的数都比a[j]大,将a[j]放在a[i]前面的话,逆序数要加上mid+1-i。因此,可以在归并排序中的合并过程中计算逆序数。
这里用洛谷的一道题来作为例题:P1908 逆序对
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 5e5 + 1;
ll n, a[maxn], ans, b[maxn];
inline ll read() {
ll x = 0, k = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') k = -1;
for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
return x * k;
}
inline void work(ll l, ll r) {
if (r - l > 0) {
ll mid = (l + r) / 2;
ll num = l, num1 = l, num2 = mid + 1;
work(l, mid);
work(mid + 1, r);
while (num1 <= mid || num2 <= r) {
if (num2 > r || (num1 <= mid && a[num1] <= a[num2]))
b[num++] = a[num1++];
else {
b[num++] = a[num2++];
ans += mid + 1 - num1;
}
}
for (int i = l; i <= r; i++) a[i] = b[i];
}
}
int main() {
n = read();
for (int i = 1; i <= n; i++) a[i] = read();
work(1, n);
printf("%lld", ans);
return 0;
}
树状数组
考虑根据值来建树状数组,初始树状数组全为0。现在按照序列从左到右将数据的值对应的位置的数加一,代表又有一个数出现。因此,在循环到第 i 项时,前 i-1 项已经加入到树状数组内了 , 树状数组内比 a[i] 大的都会与 a[i] 构成逆序对,因为它们一定出现的更早,所以产生的逆序对数量为i-query(a[i])
注:query(a[i])代表在树状数组内询问 1~a[i] 项的前缀和
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1e7 + 10;
const int maxm = 1e4 + 10;
inline ll read() {
ll x = 0, k = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') k = -1;
for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
return x * k;
}
struct node {
int val, num;
}a[maxn];
ll n, ans, c[maxn], b[maxn];
inline int lowbite(int i) {
return i & (-i);
}
inline void add(int i, int k) {
while (i <= n) {
c[i] += k;
i += lowbite(i);
}
}
inline int query(int i) {
int sum = 0;
while (i > 0) {
sum += c[i];
i -= lowbite(i);
}
return sum;
}
inline bool cmp(node x, node y) {
if (x.val == y.val) return x.num < y.num;
return x.val < y.val;
}
int main() {
n = read();
for (ll i = 1; i <= n; i++) {
a[i].val = read();
a[i].num = i;
}
sort(a + 1, a + n + 1, cmp);
for (ll i = 1; i <= n; i++) b[a[i].num] = i;
for (ll i = 1; i <= n; i++) {
add(b[i], 1);
ans += i - query(b[i]);
}
printf("%lld\n", ans);
return 0;
}