1.定義
設A[1..n]是一個包含n個不同數的數組。如果在i<j的情況下,有A[i]>A[j],則稱(i, j)為A中的一個逆序對(inversion)。
例如,A=(2, 3,8, 6, 1)的逆序對有(1, 5)、(2, 5)、(3, 4)、(3, 5)、(4, 5)共5個。
2.求解給定數組A的逆序對數目
(1)演算法思想
通過對歸併排序演算法(請參考:排序演算法(2)——歸併排序)的改寫,可以得到一個時間複雜度為Θ(nlgn)的演算法來計算數組A的逆序對數目。同樣採用分治法:
a.分解:將數組A[1..n]劃分為包含n/2個元素的子序列;
b.解決:遞迴求解兩個子序列的逆序對的數目;
c.合并:最終的逆序對數目應該為兩個子序列的逆序對數目之和再加上合并後的逆序對的數目。
因此,問題的關鍵在於求解兩個有序序列合并後的逆序對數目。
參考歸併排序演算法的Merge操作,設L[1..n1],R[1..n2],對於L中的某一元素L[i]和R中的某一元素R[j],當L[i]>R[j]時,這兩個元素在A中的下標構成一個逆序對,則對於i < ii≤ n1,同樣有L[ii] < R[j],則L[ii]與R[j]在A中的下標同樣構成逆序對。因此,當出現L[i]>R[j]時,針對於R中的元素R[j],L中可與之構成逆序對的元素數目為n1-i+1,即有n1-i+1個逆序對。
例如: A=(2, 5, 9,11, 3, 4, 8, 10)
L=(2, 5, 9, 11) R=(3, 4, 8, 10)
可知,i=2,j=1時,L[2]=5, R[1]=3, 有L[2]>R[1],則可得L[3]>R[1],L[4]>R[1],分別對應A中的逆序對(2, 5),(3, 5),(4, 5)。n1=4, i=3,共n1-i+1=4-2+1=3個逆序對。
因此,只要在歸併排序每次出現L[i]>R[j]的情況時,計算針對該R[j]的逆序對數目,再求和,即可得到兩個有序序列構成的數組的逆序對數目。逆序對問題即是對A邊進行歸併排序邊求解逆序對數目的問題,其實遞升排序本身就是在不斷消除逆序對。
(2)虛擬碼
對歸併排序的虛擬碼稍微進行修改便可得到逆序對問題的虛擬碼,如下:
INVERSION(A, p, r)
1 count ← 0
2 if p < r
3 then q ← ⎣(p+r)/2⎦
4 count← count + INVERSION (A, p, q)
5 count← count +INVERSION (A, q+1, r)
6 count← count +MERGEANDCOUNT (A, p, q, r)
7 return count
MERGEANDCOUNT(A, p, q, r)
1 count ← 0
2 n1 ← q-p+1 //計算左半部分已排序序列的長度
3 n2 ← r-q //計算右半部分已排序序列的長度
4 create arrays L[1..n1+1] and R[1..n2+1] //建立兩個數組臨時儲存兩個已排序序列,長度+1是因為最後有一個標誌位
5 for i ← 1 to n1
6 do L[i] ← A[p + i-1] //copy左半部分已排序序列到L中
7 for j ← 1 to n2
8 do R[j] ← A[q + j] //copy右半部分已排序序列到R中
9 L[n1+1] ← ∞ //L、R最後一位設定一個極大值作為標誌位
10 R[n2+1] ← ∞
11 i ← 1
12 j ← 1
13 for k ← p to r //進行合并
14 do if L[i] ≤ R[j]
15 then A[k] ← L[i]
16 i ← i + 1
17 else A[k] ← R[j]
18 j ← j + 1
19 count ← count + (n1-i+1) //計算逆序對數目
(2)代碼實現
int MergeAndCount(int A[],int p,int q,int r){ int i,j,k,count=0; int n1=q-p+1; int n2=r-q; int *L=new int[n1+1]; //開闢臨時儲存空間 int *R=new int[n2+1]; for(i=0;i<n1;i++) L[i]=A[i+p]; //數組下標從0開始時,這裡為i+p for(j=0;j<n2;j++) R[j]=A[j+q+1]; //數組下標從0開始時,這裡為就j+q+1 L[n1]=INT_MAX; //"哨兵"設定為整數的最大值,INT_MAX包含在limits.h標頭檔中 R[n2]=INT_MAX; i=0; j=0; for(k=p;k<=r;k++) //開始合并 { if(L[i]<=R[j]) A[k]=L[i++]; else { A[k]=R[j++]; count+=n1-i; //在此計算合并的逆序對數目 } } return count;}int Inversion(int A[],int p,int r){ int count=0; if(p<r) { int q=(p+r)/2; count+=Inversion(A,p,q); count+=Inversion(A,q+1,r); count+=MergeAndCount(A,p,q,r); } return count;}