求逆序对(复杂度为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
    评论
时间复杂度为O(nlogn)的算法通常用于排序问题。其中一个常见的算法是归并排序。归并排序是通过将数组划分为较小的子数组,然后逐步合并这些子数组来实现排序的。此算法时间复杂度为O(nlogn)。 归并排序的基本思想是将待排序的数组不断地对半划分,直到划分得到的子数组只包含一个元素。然后,将这些子数组两两合并,并按照从小到大的顺序进行排序。最终,合并排序后的子数组,得到一个有序的数组。 该算法的实现过程大致如下: 1. 将待排序数组划分为两个子数组,分别进行递归排序。 2. 将两个已排序的子数组合并为一个有序数组。 归并排序的时间复杂度是通过不断地将数组划分为两个子数组,直到子数组只包含一个元素,然后再将这些子数组合并的方式来实现的。因此,它的时间复杂度是O(nlogn)。其中,n是待排序数组的长度。 参考文献: 题解 在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序 通过上述的思想就可以完成一个递归的算法,因为当子数组细分到只有各元素时自然就是有序的了。 数组中的逆序对<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [时间复杂度nlogn的算法总结](https://blog.csdn.net/orangerfun/article/details/107921194)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [时间复杂度O(nlogn)的排序算法](https://blog.csdn.net/qq_43533956/article/details/123978524)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值