2-4 逆序對
設A[1...n]是一個包含n個不同數的數組,如果在i<j的情況下,有A[i]>A[j],則(i,j)就稱為A中的一個逆序對(inversion)。
a)列出數組<2, 3, 8, 6, 1>的5個逆序對
b)如果數組的元素取自集合{1,2,...,n}, 那么, 怎樣的數組含有最多的逆序對?它包含多少個逆序對?
c)插入排序的運行時間與輸入數組中逆序對的數量之間有怎樣的關系?說明你的理由。
d)給出一個算法,它能用Θ(nlgn)的最壞情況運行時間,確定n個元素的任何排列中逆序對的個數。(提示:修改合並排序)
分析與解答:
蠻力法:對於數組A[1...n],如果要確定所有的逆序對,只需要遍歷一次數組,對於以A[j]結尾的逆序只需要遍歷所有1<= i <=j
若滿足A[i] > a[j],則說明(i,j)是一個逆序對,否則不是。這是一種非常朴素的方法,整個算法的時間復雜度為Θ(n*n)
a) 采用類似上面的方法,以6結尾逆序對有(1,4),以1結尾的逆序對有(1,5),(2,5),(3,5),(4,5),總共為5個
b) 當整個數組完全逆序時,含有最多的逆序對,即A={n,...,2,1},這時
以n-1結尾的逆序對有1個,以n-2結尾的逆序對有2個,以此類推,以1結尾的逆序對有n-1個。
總共(n-1)+(n-2)+...1 = n(n-1)/2
c) 插入排序的運行時間與插入排序時的總的比較次數相關,而總共的比較次數與總的逆序對的個數相同。因此兩者成正比
在插入排序的過程中,插入A[j]需要比較的次數和已排序的A[1...j-1]中比A[j]大的元素個數相同,這等價於逆序對的個數。
d) 合並排序是采用分治法的思想,若需要排序A[p...q],則可以對半分成A[p...r]和A[r...q],然后將這有序的兩部分Merge。
而Merge的過程為Θ(n)的時間復雜度
根據主定率T(n)=2(Tn/2)+Θ(n),時間復雜度為T(n)=Θ(nlgn)。
而求整個序列中的逆序對,也可以利用分治法的思想,即
逆序對(A[p...q])= 逆序對(A[p...r])+逆序對(A[r...q])+逆序對(A[p...r], A[r...q]之間的)
結合合並排序,關鍵是求如何在Θ(n)的時間有效的求出A[p...r], A[r...q]之間的逆序對
因為在合並排序的Merge過程中,A[p...r]和A[r...q]已經有序,假設此時已經Merge到A[s...r]和A[t...q]。考慮接下來的一步:若從前者取出A[s],說明A[s]比后面的序列A[t...q]中的元素都小,不存在逆序對;若從后者取出A[t],則說明A[t]比前面的序列A[s...r]都小,即以t結尾的逆序對的數量為前者的剩余序列A[s...r]中元素的數量
Merge的過程中即可得到A[p...r], A[r...q]之間的逆序對的數量,時間復雜度亦為Θ(n), 由主定律總的時間復雜為 Θ(nlgn),這種方法要比朴素的方法 Θ(n*n)好很多。

1 //求逆序對個數(使用歸並排序) 2 //2014年8月7日 BUAA Rena 3 4 int inversion(int* A,int p,int q) 5 { 6 int sum=0; 7 int mid=(p+q)/2; 8 if (p==q) //這個是終止條件,容易忽略 9 { 10 return 0; 11 } 12 13 sum+=inversion(A,p,mid); 14 sum+=inversion(A,mid+1,q); 15 16 // 合並思想:用兩個臨時空間來存儲A的兩個子序列,然后在求解的過程中排好序再賦值給A 17 int m=mid-p+1; 18 int n=q-mid; 19 int *B=(int*)malloc(sizeof(int)*m); 20 int *C=(int*)malloc(sizeof(int)*n); 21 int i,j; 22 for(i=0;i<m+n;i++) 23 { 24 if(i<m) 25 { 26 B[i]=A[i+p]; 27 } 28 else 29 { 30 C[i-m]=A[i+p]; 31 } 32 } 33 34 i=j=0; 35 int k=p; 36 while(i<m&&j<n) 37 { 38 if(B[i]<=C[j]) 39 A[k++]=B[i++]; 40 else 41 { 42 sum+=m-i; 43 A[k++]=C[j++]; 44 } 45 } 46 if(i==m) 47 { 48 while(j<n) 49 A[k++]=C[j++]; 50 } 51 else 52 { 53 if(j==n) 54 { 55 while(i<m) 56 A[k++]=B[i++]; 57 } 58 } 59 return sum; 60 }