面試題36:數組中的逆序對

來源:互聯網
上載者:User
文章目錄
  • 參考文獻
  •  輸出結果:
題目:在數組中的兩個數字如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數。例如,有一個數組為Array[0..n] 其中有元素a[i],a[j].如果 當i<j時,a[i]>a[j],那麼我們就稱(a[i],a[j])為一個逆序對。在數組{7,5,6,4}中一共存在5對逆序對,分別是(7,6),(7,5),(7,4),(6,4),(5,4)。
參考文獻

排序演算法匯總->歸併排序

解題思路

看到這樣的題目,最簡單的想法就是遍曆每一個元素,讓其與後面的元素對比,如果大於則count++,但是這樣的時間複雜度是o(n2)。這題有更好的解決方案,時間複雜度只需要o(nlogn)。其實這道題目的思路跟歸併排序差不多,求逆序對的過程就是一個求歸併排序的過程,在求出逆序對以後,原數組變得有序,是通過歸併排序得到的。

(1)總體的意思就是將數組分成兩段,首先求段內的逆序對數量,比如下面兩段代碼就是求左右兩端數組段內的逆序對數量

inversions+=InversePairsCore(arry,start,mid,temp);//找左半段的逆序對數目inversions+=InversePairsCore(arry,mid+1,end,temp);//找右半段的逆序對數目

(2)然後求段間的逆序對數量,如下面的代碼

inversions+=MergeArray(arry,start,mid,end,temp);//在找完左右半段逆序對以後兩段數組有序,然後找兩段之間的逆序對。最小的逆序段只有一個元素。

(3)然後在求段間逆序對的時候,我們分為arry[start...mid]和arry[mid+1...end],然後設定兩個指標ij分別指向兩段數組的末尾元素,也就是i=mid,j=end。然後比較arry[i]和arry[j],

  1. 如果arry[i]>arry[j],因為兩段數組都是有序的,所以arry[i]>arry[mid+1...j],這些都是逆序對,我們統計出的逆序對為j-(mid+1)+1=j-mid。並且將大數arry[i]放入臨時數組temp[]當中,i往前移動
  2. 如果arry[i]<arry[j],則將大數arry[j]放入temp[]中,j往前移。
完整實現代碼View Code

#include<iostream>#include<stdlib.h>using namespace std;void printArray(int arry[],int len){    for(int i=0;i<len;i++)        cout<<arry[i]<<" ";    cout<<endl;}int MergeArray(int arry[],int start,int mid,int end,int temp[])//數組的歸併操作{    //int leftLen=mid-start+1;//arry[start...mid]左半段長度    //int rightLlen=end-mid;//arry[mid+1...end]右半段長度    int i=mid;    int j=end;    int k=0;//臨時數組末尾座標    int count=0;    //設定兩個指標ij分別指向兩段有序數組的頭元素,將小的那一個放入到臨時數組中去。    while(i>=start&&j>mid)    {        if(arry[i]>arry[j])        {            temp[k++]=arry[i--];//從臨時數組的最後一個位置開始排序            count+=j-mid;//因為arry[mid+1...j...end]是有序的,如果arry[i]>arry[j],那麼也大於arry[j]之前的元素,從a[mid+1...j]一共有j-(mid+1)+1=j-mid                    }        else        {            temp[k++]=arry[j--];        }    }    cout<<"調用MergeArray時的count:"<<count<<endl;    while(i>=start)//表示前半段數組中還有元素未放入臨時數組    {        temp[k++]=arry[i--];    }    while(j>mid)    {        temp[k++]=arry[j--];    }    //將臨時數組中的元素寫回到原數組當中去。    for(i=0;i<k;i++)        arry[end-i]=temp[i];    printArray(arry,8);//輸出進過一次歸併以後的數組,用於理解整體過程    return count;}int InversePairsCore(int arry[],int start,int end,int temp[]){    int inversions = 0;      if(start<end)    {        int mid=(start+end)/2;        inversions+=InversePairsCore(arry,start,mid,temp);//找左半段的逆序對數目        inversions+=InversePairsCore(arry,mid+1,end,temp);//找右半段的逆序對數目        inversions+=MergeArray(arry,start,mid,end,temp);//在找完左右半段逆序對以後兩段數組有序,然後找兩段之間的逆序對。最小的逆序段只有一個元素。    }        return inversions;}int InversePairs(int arry[],int len){    int *temp=new int[len];    int count=InversePairsCore(arry,0,len-1,temp);    delete[] temp;    return count;}void main(){    //int arry[]={7,5,6,4};    int arry[]={1,3,7,8,2,4,6,5};    int len=sizeof(arry)/sizeof(int);    //printArray(arry,len);    int count=InversePairs(arry,len);    //printArray(arry,len);    //cout<<count<<endl;    system("pause");}
 輸出結果:
調用MergeArray時的count:01 3 7 8 2 4 6 5調用MergeArray時的count:01 3 7 8 2 4 6 5調用MergeArray時的count:01 3 7 8 2 4 6 5調用MergeArray時的count:01 3 7 8 2 4 6 5調用MergeArray時的count:1//這是因為上面65之間有段內的逆序對1 3 7 8 2 4 5 6調用MergeArray時的count:01 3 7 8 2 4 5 6調用MergeArray時的count:9//這裡全部都是段間的逆序對,(3,2),(7,2),(7,4),(7,5),(7,6),(8,2),(8,4),(8,5),(8,6),一共有九個1 2 3 4 5 6 7 8逆序對數量:10

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.