求逆序对(复杂度为nlogn)

问题:

对于一个包含N个非负整数的数组A[1..n],如果有i < j,且A[ i ]>A[ j ],则称(A[ i] ,A[ j] )为数组A中的一个逆序对。

例如,数组(3,1,4,5,2)的逆序对有(3,1),(3,2),(4,2),(5,2),共4个。

给定一个数组,求该数组中包含多少个逆序对。

要求时间复杂度为nlog(n)


算法分析:

这个题目十分的经典,是归并排序的一个完美应用,分治是其主要思想,具体可以概括假设f(i,j)为i到j号元素中的逆序对个数,取一个分割点k,假设s(i,j,k)表示以k为分割点,第一个元素在i到k中,第二个元素在k+1到j中形成的逆序对数。那么我们就得到一个递归式:f(i,j)=f(i,k)+f(k+1,j)+s(i,j,k)。很自然的与归并排序联系到了一起,对于更小规模的f可以递归求解,如果对于a数组的i到k以及k+1到j两个部分元素均为有序的情况,那么对于当a[j]<a[i]的情况,必然有a[j]<a[i..k],即a[j]和i到k号元素都形成逆序对,此时只要将s(i,j,k)加上k-i+1就可以了(i到k之间的元素个数),这个过程很像归并排序的Merge的过程——比较当前i和j的状态并放入较小的。于是我们就得到了算法,和归并排序一起操作:外围设置一个计数器count,每次归并过程分为分割与合并两个部分,分割照常处理,在合并部分中的if (a[i]>a[j]) b[++l]=a[++j];中加入一个计数过程,即cnt+=k-i+1。这样当排序完成后,统计也就完成了。


算法复杂度分析:

时间上因为是和归并排序一起处理,所以时间复杂度应该是O(nlogn),同时因为是归并排序,所以还附加了O(n)的空间复杂度。


Input:

第一行,一个数字n,表示有n个数字。

第二行n个整数。


Output:

输出这n个整数组成的数组的逆序数。


</pre>代码:(c++)</p><p><span style="font-size:14px;"><strong></strong></span><pre name="code" class="cpp">#include <iostream>
using namespace std;
 
int merge (int A[], int begin, int mid, int end) {  
		static int count = 0;
        int result[end - begin + 1];  
        int i = begin;  
        int j = mid + 1;  
        int k = 0;  
        while (i <= mid && j <= end) {  
            if (A[i] <= A[j]) {  
                    result[k ++] = A[i ++];  
            }  
            else {  
                count += mid - i + 1;  
                result[k ++] = A[j ++];  
            }  
        }  
             while (j <= end)  
                 result [k ++] = A[j ++];  
            while (i <= mid)  
                 result[k ++] = A[i ++];    
        for (k = 0; k < end - begin + 1;k ++)  
            A[begin + k] = result[k];  
            return count;
    }  
/* 
 * 归并排序中调用merge函数 
 */  
int mergeSort(int a[], int begin, int end) {  
	int sum = 0; 
    if (begin < end) {  
        int mid = (begin + end)/2;  
        mergeSort (a, begin, mid);  
        mergeSort (a, mid + 1, end);  
        sum = merge (a, begin, mid, end);  
    }  
    return sum;
}

int main(int argc, char** argv) {
	int n;
	cin>>n;
	int a[n];
	for(int i = 0; i < n; i++){
		int m;
		cin>>m;
		a[i] = m;
	}
	int begin = 0, end = n - 1;
	cout<<mergeSort(a, begin, end);
	return 0;
}


  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值