逆序对:从插入排序到归并排序
来源:互联网 发布:工程成本核算软件 编辑:程序博客网 时间:2024/06/05 12:41
设A[1..n]是一个包含N个非负整数的数组。如果在i<j的情况下,有A[i]>A[j],则(i,j)就称为A中的一个逆序对(inversion)。
a)列出数组[2,3,8,6,1]的5个逆序。b)如果数组的元素取自集合{1,2,...,n},那么,怎样的数组含有最多的逆序对?它包含多少个逆序对?
c)插入排序的运行时间与输入数组中逆序对的数量之间有怎样的关系?说明你的理由。
d)给出一个算法,它能用O(nlogn)的最坏情况运行时间,确定n个元素的任何排列中逆序对的数目(提示:修改归并排序)
——《算法导论》,思考题2-4
逆序对的应用很多,比如各类OJ中的逆序对题目:http://www.cppblog.com/ickchen2/articles/62422.html,再比如《编程之美》1.7光影切割问题解法二的求交点个数。从上面的思考题入手来理解逆序对算法很简单,这也是标题中所展示的思路历程。本文主要面对的是之前对逆序对基本没接触和不了解O(nlogn)解法的读者,可能显得有些啰嗦。
问题a)直接根据定义可得<2,1>,<3,1>,<8,6>,<8,1>,<6,1>。
问题b)为了逆序对最多,那么应使任一个数在所有比它小的数前面,从而构成所有可能逆序,即[n,n-1,...,1],这样一共有(n-1)+(n-2) + ... + 1 =n(n-1)/2个。
问题c),在插入排序进行时,数组分为两部分:已排序部分和待排序部分。每次将待排序部分的第一个元素插入到已排序部分时,需要找出其插入的位置,并把这之后的已排序元素依次后移。并且,对于一个元素,插入过程中后移的元素数目就是它在原数组中它前面的逆序对的数目。这是因为,根据逆序对定义,可以写出O(n2)的检测方式,二者是一样的。
for(i=0;j<n;j++) for(i=0;i<j;i++) if(A[i]>A[j]) count++;
其实对于问题c,本意并不是告诉读者使用插入排序来找逆序对:同样是O(n2)的算法,这样做没有任何改进之处;而是在于启发对问题d)的解答。
1.在归并排序中,同样是对一个数组分为两段处理,在处理这两段时,并不会影响右段元素与左段元素的逆序关系,只有在归并时才会改变。
2.归并时的改变方式和插入排序是类似的:右段中取出元素放在左段其余所有元素前面时,相当于左段整体后移,后移的元素数就是这个逆序数。
3.由于归并排序使用的是分治法,将每次归并的逆序数累加,最后结果就是总的逆序数。并且,归并排序的时间复杂度是O(nlogn),优于插入排序。
根据以上的探讨,归并排序稍作修改,就获得了时间复杂度为O(nlogn)的寻找逆序对总数的算法了,下面是一个简单示例。
#include <stdio.h>#include <stdlib.h>#define MAXNUM 65535#define length 8static int data[length] ={5,2,4,7,1,3,2,6};//#define length 5//static int data[length] ={2,3,8,6,1};int show_out(int *array,int n);int merge(int *array, int nBegin, int nMid, int nEnd) { int n1,n2; int i,j,k; int count =0; n1 = nMid-nBegin+1; n2= nEnd-nMid; int *left,*right; left = (int *)malloc((n1+1) * sizeof(int)); right = (int *)malloc ((n2+1) * sizeof(int)); for (i=0;i<n1;i++) left[i] = array[nBegin+i]; for (j=0;j<n2;j++) right[j] = array[nMid+j+1]; left[n1] = MAXNUM; right[n2] = MAXNUM; i = 0; j = 0; for (k = nBegin;k<=nEnd;k++) { if (left[i] <= right[j]) { array[k] = left[i]; i++; //从left中拷贝至array,没有改变逆序数 } else { array[k] = right[j]; j++; count += n1-i; //left中n1-i个在right[j]前面 //拷贝时会减少n1-i个逆序数 } } free(left); free(right); show_out(array,length); return count;}int inversion(int *array, int nBegin, int nEnd) { int nMid,count = 0; if (nBegin < nEnd) { nMid = (nEnd - nBegin>>1) + nBegin; //nMid = (nBegin+nEnd)/2; count = inversion(array,nBegin,nMid) + inversion(array,nMid+1,nEnd) + merge(array,nBegin,nMid,nEnd); } return count;}int show_out(int *array,int n) { int k; for (k = 0; k<n; k++) printf("%d ",array[k]); printf("\n");}int main() { int result; result = inversion(data,0,length-1); printf("inversions:%d\n",result); return result; }
<script type="text/javascript"><!--google_ad_client = "ca-pub-1944176156128447";/* cnblogs 首页横幅 */google_ad_slot = "5419468456";google_ad_width = 728;google_ad_height = 90;//--></script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
- 逆序对:从插入排序到归并排序
- 逆序对:从插入排序到归并排序
- 从归并排序到数列的逆序数对
- 从归并排序到逆序对数目计算。
- 逆序对 归并排序
- 归并排序 & 逆序对
- 归并排序--逆序对
- 归并排序&归并排序求逆序对
- 从归并排序到数列的逆序数对(微软笔试题)
- 从归并排序到数列的逆序数对(微软笔试题)
- 从归并排序到数列的逆序数对(微软笔试题) .
- 从归并排序到数列的逆序数对(微软笔试题)
- 归并排序求逆序对
- 【归并排序】【逆序对】序列
- 归并排序与逆序对
- 归并排序求逆序对
- 归并排序求逆序对~~~
- 归并排序求逆序对
- iOS抽奖程序
- 那些争议最大的编程观点
- python Json的一点收获,自定义序列化方法
- ubuntu 安装jdk7总结
- NYOJ42-一笔画问题
- 逆序对:从插入排序到归并排序
- Joomla博客式文章列表
- java实现某个月的第一天/中间结束/中间开始/下个月的第一天的算法
- small rtos1.20.3的一点小问题
- 读书笔记---鸟哥linux---第六章---linux的文件权限与目录配置
- 不同用户地图界面不同
- Cocos2d-x 实例源码分析之二 小实例的主框架
- 敏捷软件开发揭秘
- MFC下的FTP VC++