您的位置:首页 > 其它

求逆序对算法总结

2013-05-11 10:11 246 查看
设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[/code]          
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[/code]          
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  逆序对算法