求逆序對算法總結

設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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章