歸併排序法計算逆序對數

2022-02-20 22:12:31 字數 3288 閱讀 9764

今天遇到求逆序對的問題,經過一番思索之後,特意來總結一下。因為也學習到了很多方法,以前自己一些百思不得其解的問

題也有了解答。

分析:題目中說使用插入排序,也就是在排序過程中計算交換的次數,按照插入排序的原理,先定第乙個,再定前兩個的順序,以此類推,只要交換了,我的次數就加一,但實際上,我們一直按照原始序列的順序一直在往後走,所以(好,重點來了)我們要插入的就是前面比我大的數字前面的位置,也就是說,我需要交換的次數就是前面比我大的數字的個數,那麼我考慮那就沒必要進行交換了,直接進行和前面的數字進行比較就可以了啊,只要前面有比你現在所比較的數大,則加一。其實這很像我們線代學過的逆序數

,就是求逆序數的個數。

接下來就是寫**:

1 #include2

intmain()

1415

for(int k=1; k < m; k++) 19}

20}21 printf("

%d\n

",count);

2223

return0;

24 }

這裡的**就是普普通通的**,通俗易懂。當然,兩個迴圈那裡可以進行演算法的優化,有心的讀者自己去嘗試吧。或者說看接下來的內容也可以收穫另一種求逆序數的方法。 

好,接下來,我們講述重點的問題,相信很多人都可以解決前面的問題,但接下來的問題就不是那麼容易了,需要思考與一些**技巧。

注意:hint : 直接使用兩層迴圈來找答案的話會超過系統時間限制。

所以就必須尋求演算法複雜度低的演算法,按照提示,說在歸併的過程當中計算逆序數對,首先要熟悉歸併排序的原理,再結合問題來看。於是我在紙上進行了演算,先不斷地進行二分,然後就兩個兩個相比較,就是分為兩組,每組乙個數,如果後面這個比前面的大,那就是乙個逆序數對,然後就加一,並且將小的數字放在前面,於是合併,就得到了包含兩個數的有序的一組數。接下來就是比較每組兩個數的比較,如果第二組的第乙個數大於第一組的第乙個數,就加二,因為它比前面這組所有數都小。然後歸併。以此類推。原則就是:如果後面這組數的某個數比前面這組的第i個數小,則逆序對數加上(mid-i+1)。

雖然明白了原理,對於歸併不熟悉的同學,我覺得還是比較難的,特別是其中的一些技巧。

不多說。先分析**如何寫,先寫框架:

1 #include2

3int count = 0

;//逆序數對

4void mergesort(int lo,int

hi) 78

intmain()

1516 mergesort(0,n-1

);//歸併排序

1718 printf("

%d\n

",count);

19return0;

20 }

這部分框架應該都能看懂。接下來講述歸併。首先原理就是先二分,分別排序,後歸併。

1

void mergesort(int lo,int

hi)

11 }

其實這差不多也是個框架,只不過注意一下 lo < hi 這個條件。

然後重點在於歸併這部分,設定標記點,i = lo  和 j = mid+1  迴圈的條件應該是

1

int i =lo;

2int j = mid + 1;3

while(i <= mid&&j <=hi)

如果後面前面這組數的第i個數大於後面某個數,count就加mid-i+1。當然歸併時需要乙個臨時陣列來儲存這些改變位置的數,

1

int i =lo;

2int j = mid + 1;3

int x =lo;45

while(i <= mid&&j <=hi)

10else

13 }

當然,這還有個要注意的地方,如果前面這組數已經排完了,然後後面這組數還沒完就已經退出了迴圈,那這個臨時陣列就沒有歸併所有的數進來,就不完整。此時就應該加上

1

while(i <= mid) temp[x++] = ch[i++];

2while(j <= hi) temp[x++] = ch[j++];

然後當然我們還要把這個臨時陣列的值又返回到原來的陣列中,以便於這個陣列在下一輪進行歸併。

1

for(int k = lo; k <= hi ; k++)

2 ch[k] = temp[k];

好,這樣,我們就完成整個**的編寫

這是完整的源**:

1 #include2

3void merge(int ,int ,int);4

void mergesort(int ,int);5

6int ch[20000],temp[20000

];//最大有20000個數,注意這裡要是全域性變數,易於使用。

7int count = 0

;//逆序數,一定要是全域性變數,這樣就可以無論怎麼遞迴都會一直加。原先的想法就是遞迴中返回逆序對的數,不斷累加,實現起來比這個困難。這個直接就是全域性變數,方便簡潔。89

void mergesort(int lo,int

hi) 19}

2021

void merge(int lo,int mid,int

hi) else33}

3435

while(i <= mid) temp[x++] = ch[i++];

36while(j <= hi) temp[x++] = ch[j++];

3738

for(int k = lo; k <= hi ; k++)

39 ch[k] =temp[k];

4041}42

intmain()

4950 mergesort(0,n-1

);51

52 printf("

%d\n

",count);

53return0;

54 }

這裡帶給我最大的收穫就是count是全域性變數,因此才可以在不斷的遞迴中一直累加,我原先的想法就是在遞迴中看能不能返回逆序對的個數,或者在引數中間加入逆序對的個數一直傳遞。這次終於得到了解答,還有這個歸併時他建立的臨時陣列也很巧妙,最終又賦值給原陣列。最棒的就是遞迴這部分,以前老是理不清,想不清,看來以後得多用用遞迴。

2016-02-25  12:37:30

歸併排序計算逆序對數目

陣列a中的乙個逆序對 a i a j 是ia j 平凡的方法是遍歷所有數對,時間複雜度為o n 2 利用歸併排序的思想可以降至o nlgn include include include include include using namespace std 平凡的計算逆序數的方法 比較陣列中的所有...

歸併排序求逆序對數

參考部落格 歸併排序求逆序對數 include include include includeusing namespace std 歸併排序是借助乙個輔助陣列來進行排序 int ans 0 void merge sort int a,int l,int r,int t a是原陣列,t是輔助陣列 i...

歸併排序求解逆序對數

定義 逆序對就是對於ia j 這樣的數對在序列中的個數。求解方法 歸併排序是採用分治的思想劃分數列,然後將兩路有序的數列合併。通過劃分和合併的遞迴呼叫來完成排序。在合併的過程中,兩個數列中的元素的相對位置不會發生改變 這裡只是前後關係 而且如果後乙個數列b中某個元素b在需要先放入 優先於前乙個數列a...