您的位置:首页 > 其它

求逆序对(复杂度为nlogn)

2016-03-20 16:54 253 查看
问题:

对于一个包含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
;
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: