求逆序對(inversion)的個數


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 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM