zoukankan      html  css  js  c++  java
  • 求逆序对算法总结

    设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

  • 相关阅读:
    使用 cordova-plugin-wechat 分享返回后闪退解决方法
    恢复删除的表
    移动端还原设计图
    js文本差异比较
    windows使用nvm安装nodejs后升级npm报错
    windows添加右键菜单"在此处打开cmd窗口"
    cordova热更新
    js变量提升
    c# 判断字符串是否是日期格式需要注意的一点小问题
    剑指offer_和为S的两个数字
  • 原文地址:https://www.cnblogs.com/xuddong/p/3072449.html
Copyright © 2011-2022 走看看