题目描述
给定一个长度为 n
的整数数列,请你计算数列中的逆序对的数量
。
逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i<j 且 a[i]>a[j]
,则其为一个逆序对;否则不是。
输入格式:
第一行包含整数 n,表示数列的长度。
第二行包含 n个整数,表示整个数列。
输出格式:
输出一个整数,表示逆序对的个数。
数据范围:
1≤n≤100000
输入样例:
6
2 3 4 5 6 1
输出样例:
5
思路分析
利用“分治”的方式,将序列进行分解为LA和LB,则逆序对数=LA中的逆序对数+LB中的逆序对数+LA中相对于LB中的逆序对数。
假设当LA和LB已为按非递减顺序的有序序列,从左至右开始查找,当查找到LB中的某个数x小于LA中的数y时,则可确定从LA中的y开始到LA的最后一个元素z,其所有数都比LB中的x大,此时便得到逆序数个数count = z - y + 1,以此类推,当完成LA和LB的所有查找后,便找到LA中相对于LB所产生的逆序对数。
因此采用归并排序算法
参考文章:
算法导论习题—求n个元素任何排列中逆序对的数量
算法实现
#include <stdio.h>
// 假设全部逆序则为n*n=(10^10)/2=5*10^9,使用int越界因此使用long long
typedef long long LL;
const int N = 1e5 + 10;
int tmp[N];
LL merge_sort(int q[], int l, int r){
if(l >= r) return 0;
int mid = l + r >> 1;
// 获取左侧序列和右侧序列逆序对数量之和
LL res = merge_sort(q, l, mid) + merge_sort(q, mid + 1, r);
int k = 0, i = l, j = mid + 1;
while(i <= mid && j <= r){
// 第一个序列中的前i个均小于第二个数组,则无逆序对
if(q[i] <= q[j]) tmp[k++] = q[i++];
else{
// 第二个数组的第j个小于第一个数组从下标i开始之后的所有元素
tmp[k++] = q[j++];
res += mid - i + 1;
}
}
while(i <= mid) tmp[k++] = q[i++];
while(j <= r) tmp[k++] = q[j++];
for(k = 0,i = l; i <= r; k++, i++) q[i] = tmp[k];
return res;
}
int main(){
int n; scanf("%d",&n);
int q[N]; for(int i = 0; i < n; i++) scanf("%d",&q[i]);
LL res = merge_sort(q, 0 ,n-1);
printf("%ld", res);
return 0;
}
算法模板:
37、【排序】归并排序(C语言版)