题目:利用归并排序求解一个数组中的逆序数对
分析:
1. 什么是逆序数对,例如给定数组{7, 5, 6, 4},对于每个数num,如果num之前有多少个数大于num则说明num这个数构成逆序数对有多少个
7有0个,5有1个,6有1个,4有三个,因此数组中总的逆序数对为5。
2. 怎么利用归并排序来求逆序数对呢?
(1)假设数组为{7, 5, 6, 4}
(2)归并排序的递归树如下
(3)求逆序数对是在合并左右两个子序列的时候。在merge函数中,左右子序列都是各自有序。
在合并两个子序列的时候我们是从前往后枚举两个子序列,每次求两个数中的最小值存放到另一个数组中。
假设此时左序列数为arr[i],有序列为arr[j]。
如果arr[i] > arr[j],则可以知道左序列中从i开始到结束的所有元素都和arr[j]构成逆序数对。
如果arr[i] <= arr[j]则无法构成逆序数对。
(4)根据第三点,我们可以对数组{7,5,6,4}进行验证
合并第三层:7 > 5逆序数加1;6 > 4逆序数加1。合并完第三层为(5 7) 和 (4 6)
合并第二层:5 > 4逆序数加2;7 > 4逆序数加1。合并完第二层为(4 5 6 7)
第一层不用合并,所以总的逆序数对为5。
3. 代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int ans;
//合并左右子序列
void Merge(int *arrNum, int l, int mid, int r){
//数据不合法
if(arrNum == NULL || l > r){
return;
}
int i = l;
int j = mid+1;
int pos = l;
int tmpArr[N];
//枚举
while((i <= mid) && (j <= r)){
if(arrNum[i] <= arrNum[j]){
tmpArr[pos++] = arrNum[i++];
}
else{
ans += (mid-i+1);//加上逆序数对
tmpArr[pos++] = arrNum[j++];
}
}
//剩下的直接插入数组末尾
while(i <= mid){
tmpArr[pos++] = arrNum[i++];
}
while(j <= r){
tmpArr[pos++] = arrNum[j++];
}
//复制回新的数组
for(int k = l; k <= r; k++){
arrNum[k] = tmpArr[k];
}
}
//求逆序数对
int GetReverseNumber(int *arrNum, int l, int r){
//数据不合法
if(arrNum == NULL || l >= r){
return 0;
}
int mid = (l+r)>>1;
GetReverseNumber(arrNum, l, mid);
GetReverseNumber(arrNum, mid+1, r);
Merge(arrNum, l, mid, r);
}
int main(){
int arrNum[] = {7,5,6,4};
//求逆序数对
ans = 0;
GetReverseNumber(arrNum, 0, 3);
cout<<ans<<endl;
getchar();
return 0;
}
/*
输出
5
*/