求逆序对算法总结

设A[1..n]是一个包含N个非负整数的数组。如果在i〈 j的情况下,有A〉A[j],则(i,j)就称为A中的一个逆序对。
例如,数组(3,1,4,5,2)的“逆序对”有<3,1>,<3,2><4,2><5,2>,共4个。
那么该如何求出给定一个数列包含逆序对个数?

首先最简单的方法,直接遍历,时间复杂度为O(n^2)

源码如下:

   1: //最简单的办法,直接枚举遍历
   2: int count_inverse_pairs(int a[],int first, int last)
   3: {
   4:     int count = 0;
   5:     int i;
   6:     while(first < last)
   7:     {
   8:         i = first +1;
   9:     //    int cout_tmp = 0;
  10:         while(i <= last)
  11:         {            
  12:             if(a[i] <  a[first]){
  13:                 count++;
  14:         //        ++ cout_tmp;
  15:             }            
  16:             i++;
  17:         }
  18:         //cout<<cout_tmp<<endl;
  19:         first++;
  20:     }
  21:     cout << count << endl;
  22:     return count;
  23: }

其时间复杂度比较的高。一般不值得使用,

可以根据分析插入排序的特点,重新设计,插入排序,每个数字向前移动的次数,就是以该元素为第一元素的逆序对的数目,其时间复杂度和插入排序是一样的O(n^2),具体的源代码如下所示(直接来自于网络):

   1: /*************************************************
   2: 直接利用插入排序求逆序对,时间复杂度O(N^2)
   3: Function: InsertionSort_Inversion
   4: Description:对elems进行从小到大的排序来计算逆序对数量
   5: Input:    elems:ElementType类型的数组
   6:         size:elems的大小
   7: Output: 逆序对的数量
   8: *************************************************/
   9: int InsertionSort_Inversion(ElementType elems[], int size)
  10: {
  11:     int inversion = 0;
  12:     for (int i = 1; i < size; i++)
  13:     {
  14:     //    int tmp_count= 0;
  15:         int j = i - 1;                    //j表示正在和key比较大小的数组元素的序号
  16:         ElementType key = elems[i];
  17:         while ( key < elems[j] && j >= 0)
  18:         {
  19:             elems[j + 1] = elems[j];
  20:             j--;
  21:             inversion++;
  22:     //        ++tmp_count;
  23:         }
  24:     //    cout<<tmp_count <<endl;    
  25:         elems[j + 1] = key;
  26:     }
  27:     return inversion;
  28: }

此种方法充分利用到了所学到的排序只是,但是时间复杂度依旧很大,于是很多人又想出了另一种方案,依据归并排序的特点就行求解,源代码(直接来源于网络)如下:

   1: /*************************************************
   2: 利用归并排序求解
   3: Function: MergeSort_Inversion
   4: Description:对elems进行从小到大的排序来计算逆序对数量
   5: Input:    elems:ElementType类型的数组
   6:         begin:elems的开始序号
   7:         end:elems的结束序号
   8: Output: 逆序对的数量
   9: *************************************************/
  10: int MergeSort_Inversion(ElementType elems[], int begin, int end)
  11: {
  12:     int inversion = 0;
  13:     if (end == begin) 
  14:     {
  15:         return 0;
  16:     }
  17:     else if (end == begin + 1)
  18:     {
  19:         if (elems[begin] > elems[end])
  20:         {
  21:             ElementType tempElement = elems[begin];
  22:             elems[begin] = elems[end];
  23:             elems[end] = tempElement;
  24:             inversion++;
  25:         }
  26:     }
  27:     else 
  28:     {
  29:         inversion += MergeSort_Inversion(elems, begin, (begin + end) / 2);
  30:         inversion += MergeSort_Inversion(elems, (begin + end) / 2 + 1, end);
  31:         inversion += Merge_Inversion(elems, begin, (begin + end) / 2, end);
  32:     }
  33:     return inversion;
  34: }
  35:  
  36:  
  37: /*************************************************
  38: Function: Merge_Inversion
  39: Description:对elems中begin到middle 和middle到end 两部分进行从小到大的合并,同时计算逆序对数量
  40: Input:    elems:ElementType类型的数组
  41:         begin:elems的开始序号
  42:         middle:elems的分割序号
  43:         end:elems的结束序号
  44: Output: 逆序对的数量
  45: *************************************************/
  46: int Merge_Inversion(ElementType elems[], int begin, int middle, int end)
  47: {
  48:     int inversion = 0;
  49:     int *tempElems = new int[end - begin + 1] ;
  50:     int pa = begin;        //第一个子数组的开始序号
  51:     int pb = middle + 1;    //第二个子数组的开始序号
  52:     int ptemp = 0;        //临时数组的开始序号
  53:  
  54:     while (pa <= middle && pb <= end)
  55:     {
  56:         if (elems[pa] > elems[pb])
  57:         {
  58:             tempElems[ptemp++] = elems[pb++];
  59:             inversion += (middle + 1 - pa);
  60:         }
  61:         else 
  62:         {
  63:             tempElems[ptemp++] = elems[pa++];
  64:         }
  65:     }
  66:     
  67:     if (pa > middle)
  68:     {
  69:         for ( ; pb <= end; pb++)
  70:         {
  71:             tempElems[ptemp++] = elems[pb];
  72:         }
  73:     }
  74:     else if(pb > end)
  75:     {
  76:         for ( ; pa <= middle; pa++)
  77:         {
  78:             tempElems[ptemp++] = elems[pa];
  79:         }
  80:     }
  81:     //copy tempElems to lems
  82:     for (int i = 0; i < end - begin + 1; i++)
  83:     {
  84:         elems[begin + i] = tempElems[i];
  85:     }
  86:     delete []tempElems;
  87:     return inversion;
  88: }

此外,还有气的解法,有人提出树状数组的解法,还有二分加快排的解法,自己去看吧,先说这么多了。

程序示例,所有的源代码(VS2010编译):

   1: // code-summary.cpp : 定义控制台应用程序的入口点。
   2:  
   3: /**************************************************************************
   4:     * Copyright (c) 2013,  All rights reserved.
   5:     * 文件名称    : code-summary.cpp
   6:     * 文件标识    :
   7:     * 摘    要    : 求逆序对个数的算法
   8:     * 
   9:     * 当前版本    : Ver 1.0
  10:     * 作者    : 徐冬冬
  11:     * 完成日期    : 2013/05/10
  12:     *
  13:     * 取代版本    : 
  14:     * 原作者    :
  15:     * 完成日期    :  
  16:     * 开放版权  : GNU General Public License GPLv3
  17: *************************************************************************/
  18: #include "stdafx.h"
  19:  
  20: #include <iostream>
  21: #include <random>
  22: #include <stdlib.h>
  23: using namespace std;
  24:  
  25: typedef int ElementType;
  26:  
  27: int InsertionSort_Inversion(ElementType elems[], int size);
  28: int MergeSort_Inversion(ElementType elems[], int begin, int end);
  29: int Merge_Inversion(ElementType elems[], int begin, int middle, int end);
  30:  
  31: //求逆序对个数
  32: //初始化数组
  33: void init(int a[], int len)
  34: {
  35:     int i =0;
  36:     for( i=0;  i<len ;i++)
  37:     {
  38:         a[i]= rand();
  39:     }
  40:     return ;    
  41: }
  42: ///遍历数组,打印输出
  43: void print(int *a, int len)
  44: {
  45:     int i =0;
  46:     for( i=0;  i<len; i++)
  47:     {
  48:         cout << a[i] <<'\t';
  49:     }
  50:     cout << endl;
  51:     return ;    
  52: }
  53: //交换两个元素
  54: void swap(int &a, int &b)
  55: {
  56:     int tmp =a;
  57:     a = b;
  58:     b=tmp;
  59:     return ;
  60: }
  61: //最简单的办法,直接枚举遍历
  62: int count_inverse_pairs(int a[],int first, int last)
  63: {
  64:     int count = 0;
  65:     int i;
  66:     while(first < last)
  67:     {
  68:         i = first +1;
  69:     //    int cout_tmp = 0;
  70:         while(i <= last)
  71:         {            
  72:             if(a[i] <  a[first]){
  73:                 count++;
  74:         //        ++ cout_tmp;
  75:             }            
  76:             i++;
  77:         }
  78:         //cout<<cout_tmp<<endl;
  79:         first++;
  80:     }
  81:     cout << count << endl;
  82:     return count;
  83: }
  84:  
  85: /*************************************************
  86: 直接利用插入排序求逆序对,时间复杂度O(N^2)
  87: Function: InsertionSort_Inversion
  88: Description:对elems进行从小到大的排序来计算逆序对数量
  89: Input:    elems:ElementType类型的数组
  90:         size:elems的大小
  91: Output: 逆序对的数量
  92: *************************************************/
  93: int InsertionSort_Inversion(ElementType elems[], int size)
  94: {
  95:     int inversion = 0;
  96:     for (int i = 1; i < size; i++)
  97:     {
  98:     //    int tmp_count= 0;
  99:         int j = i - 1;                    //j表示正在和key比较大小的数组元素的序号
 100:         ElementType key = elems[i];
 101:         while ( key < elems[j] && j >= 0)
 102:         {
 103:             elems[j + 1] = elems[j];
 104:             j--;
 105:             inversion++;
 106:     //        ++tmp_count;
 107:         }
 108:     //    cout<<tmp_count <<endl;    
 109:         elems[j + 1] = key;
 110:     }
 111:     return inversion;
 112: }
 113:  
 114: /*************************************************
 115: 利用归并排序求解
 116: Function: MergeSort_Inversion
 117: Description:对elems进行从小到大的排序来计算逆序对数量
 118: Input:    elems:ElementType类型的数组
 119:         begin:elems的开始序号
 120:         end:elems的结束序号
 121: Output: 逆序对的数量
 122: *************************************************/
 123: int MergeSort_Inversion(ElementType elems[], int begin, int end)
 124: {
 125:     int inversion = 0;
 126:     if (end == begin) 
 127:     {
 128:         return 0;
 129:     }
 130:     else if (end == begin + 1)
 131:     {
 132:         if (elems[begin] > elems[end])
 133:         {
 134:             ElementType tempElement = elems[begin];
 135:             elems[begin] = elems[end];
 136:             elems[end] = tempElement;
 137:             inversion++;
 138:         }
 139:     }
 140:     else 
 141:     {
 142:         inversion += MergeSort_Inversion(elems, begin, (begin + end) / 2);
 143:         inversion += MergeSort_Inversion(elems, (begin + end) / 2 + 1, end);
 144:         inversion += Merge_Inversion(elems, begin, (begin + end) / 2, end);
 145:     }
 146:     return inversion;
 147: }
 148:  
 149:  
 150: /*************************************************
 151: Function: Merge_Inversion
 152: Description:对elems中begin到middle 和middle到end 两部分进行从小到大的合并,同时计算逆序对数量
 153: Input:    elems:ElementType类型的数组
 154:         begin:elems的开始序号
 155:         middle:elems的分割序号
 156:         end:elems的结束序号
 157: Output: 逆序对的数量
 158: *************************************************/
 159: int Merge_Inversion(ElementType elems[], int begin, int middle, int end)
 160: {
 161:     int inversion = 0;
 162:     int *tempElems = new int[end - begin + 1] ;
 163:     int pa = begin;        //第一个子数组的开始序号
 164:     int pb = middle + 1;    //第二个子数组的开始序号
 165:     int ptemp = 0;        //临时数组的开始序号
 166:  
 167:     while (pa <= middle && pb <= end)
 168:     {
 169:         if (elems[pa] > elems[pb])
 170:         {
 171:             tempElems[ptemp++] = elems[pb++];
 172:             inversion += (middle + 1 - pa);
 173:         }
 174:         else 
 175:         {
 176:             tempElems[ptemp++] = elems[pa++];
 177:         }
 178:     }
 179:     
 180:     if (pa > middle)
 181:     {
 182:         for ( ; pb <= end; pb++)
 183:         {
 184:             tempElems[ptemp++] = elems[pb];
 185:         }
 186:     }
 187:     else if(pb > end)
 188:     {
 189:         for ( ; pa <= middle; pa++)
 190:         {
 191:             tempElems[ptemp++] = elems[pa];
 192:         }
 193:     }
 194:     //copy tempElems to lems
 195:     for (int i = 0; i < end - begin + 1; i++)
 196:     {
 197:         elems[begin + i] = tempElems[i];
 198:     }
 199:     delete []tempElems;
 200:     return inversion;
 201: }
 202:  
 203:  
 204: int _tmain(int argc, _TCHAR* argv[])
 205: {
 206:     int *arr = new int[10];
 207:     init(arr, 10);
 208:     print(arr, 10);
 209:     int  count= count_inverse_pairs( arr,0,9);
 210:     print(arr, 10 );
 211:  
 212:     print(arr, 10);
 213:     count = InsertionSort_Inversion(arr, 10);
 214:     cout<< count <<endl;    
 215:     print(arr, 10 );        
 216:  
 217:     print(arr, 10);
 218:     count = MergeSort_Inversion(arr, 0, 9);
 219:     cout<< count <<endl;    
 220:     print(arr, 10 );        
 221:  
 222:     system("pause");
 223:     return 0;
 224: }
 225:  
 226:  
 227: /************************************************************************/
 228: /**
 229: 树状数组 求解,没看到懂
 230: http://blog.csdn.net/weiguang_123/article/details/7895848
 231: 
 232: 其实这些都用不上,二分加快排最简单,时间复杂度比树状数组小得多。
 233: 详见以下:
 234: program liujiu;
 235: var n,m:longint;
 236: w,s,f,pai:array[0..100010]of int64;
 237: ans:int64;
 238: 
 239: procedure init;
 240: var i:longint;
 241: begin
 242: assign(input,'snooker.in');
 243: assign(output,'snooker.out');
 244: reset(input);
 245: rewrite(output);
 246: fillchar(w,sizeof(w),0);
 247: fillchar(s,sizeof(s),0);
 248: fillchar(f,sizeof(f),0);
 249: randomize;
 250: s[0]:=0;
 251: readln(n,m);
 252: for i:=1 to n do
 253: begin
 254: read(w[i]);
 255: w[i]:=w[i]-m;
 256: s[i]:=s[i-1]+w[i];
 257: end;
 258: end;
 259: 
 260: procedure sort(l,r:longint);
 261: var i,j,k,le:longint;
 262: begin
 263: i:=l;
 264: j:=r;
 265: k:=pai[random(r-l)+l];
 266: while i<=j do
 267: begin
 268: while pai[i]<k do inc(i);
 269: while pai[j]>k do dec(j);
 270: if i<=j then
 271: begin
 272: le:=pai[i];
 273: pai[i]:=pai[j];
 274: pai[j]:=le;
 275: inc(i);
 276: dec(j);
 277: end;
 278: end;
 279: if i<r then sort(i,r);
 280: if j>l then sort(l,j);
 281: end;
 282: 
 283: 
 284: procedure   work(left,right:longint);
 285: var i,t,j,mid:longint;
 286: begin
 287: mid:=(left+right)div 2;
 288: for i:=left to right do pai[i]:=s[i];
 289: sort(left,mid);
 290: sort(mid+1,right);
 291: i:=left;
 292: j:=mid+1;
 293: while (j<=right)and(i<=mid)do
 294: begin
 295: while (pai[j]>pai[i])and(j<=right)
 296: and(i<=mid)do
 297: begin
 298: inc(ans,(right-j+1));
 299: inc(i);
 300: end;
 301: inc(j);
 302: end;
 303: if left<mid then work(left,mid);
 304: if mid+1<right then work(mid+1,right);
 305: end;
 306: 
 307: procedure main;
 308: var i,j,k,l:longint;
 309: begin
 310: ans:=0;
 311: work(0,n);
 312: writeln(ans);
 313: close(input);
 314: close(output);
 315: end;
 316: 
 317: begin
 318: init;
 319: main;
 320: end.
 321:                             http://tieba.baidu.com/p/934583490
 322:                 */
 323: /************************************************************************/

 

参考:

http://www.cnblogs.com/XiongHarry/archive/2008/11/06/1327732.html

http://blog.csdn.net/fanhj__2007/article/details/5544526

http://blog.sina.com.cn/s/blog_69dcc82d0100vnff.html

树状数组实现的网络参考(我自己还没有弄懂);

http://www.cppblog.com/acronix/archive/2010/10/29/131753.aspx?opt=admin

http://blog.csdn.net/weiguang_123/article/details/7895848

posted @ 2013-05-11 10:11  远东悟语  阅读(3006)  评论(1编辑  收藏  举报

我是徐冬冬