設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