求逆序对算法总结
设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
雨,静静的飘扬;
心,慢慢的行走;
程序人生,人生迈进。