题目:利用归并排序求解一个数组中的逆序数对
分析:
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 */