【33】利用归并排序求逆序数对


题目:利用归并排序求解一个数组中的逆序数对


分析:

1. 什么是逆序数对,例如给定数组{7, 5, 6, 4},对于每个数num,如果num之前有多少个数大于num则说明num这个数构成逆序数对有多少个

    7有0个,5有1个,6有1个,4有三个,因此数组中总的逆序数对为5。


2. 怎么利用归并排序来求逆序数对呢?

   (1)假设数组为{7, 5, 6, 4}

   (2)归并排序的递归树如下

       【33】利用归并排序求逆序数对

   (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 
*/


上一篇:请参考rhel7 安装 oracle 18c rac(03 dbca 建立数据库)


下一篇:Java并发编程总结2——慎用CAS(转)