求逆序对(复杂度为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个整数组成的数组的逆序数。
对于一个包含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; }
相关文章推荐
- Android学习(一)
- 五边形
- 安卓adb.exe无法启动
- 配置App真机测试证书的流程 一览
- <香港科技大学html+css+js课堂笔记>week1--CSS部分
- 《PCANet: A Simple Deep Learning Baseline for Image Classification》
- Mysql视图使用总结
- A tutorial on Principle Component Analysis
- linux 常用命令
- 「Emacs org-mode(一)」用Org-mode实现GTD
- css中的div布局之巧用div
- 求自定类型元素序列的中位数
- 20145233韩昊辰 第(三)周总结
- 理解socket的阻塞
- (一)MongoDB简介
- SQL Server 未删除任何行
- NSAttributedString 的21种属性 详解
- ID3算法
- maven的Generating Project in Batch mode 卡住问题
- LightOJ 1341 - Aladdin and the Flying Carpet