今天遇到求逆序對的問題,經過一番思索之後,特意來總結一下。因為也學習到了很多方法,以前自己一些百思不得其解的問
題也有了解答。
分析:題目中說使用插入排序,也就是在排序過程中計算交換的次數,按照插入排序的原理,先定第乙個,再定前兩個的順序,以此類推,只要交換了,我的次數就加一,但實際上,我們一直按照原始序列的順序一直在往後走,所以(好,重點來了)我們要插入的就是前面比我大的數字前面的位置,也就是說,我需要交換的次數就是前面比我大的數字的個數,那麼我考慮那就沒必要進行交換了,直接進行和前面的數字進行比較就可以了啊,只要前面有比你現在所比較的數大,則加一。其實這很像我們線代學過的逆序數
,就是求逆序數的個數。
接下來就是寫**:
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其實這差不多也是個框架,只不過注意一下 lo < hi 這個條件。void mergesort(int lo,int
hi)
11 }
然後重點在於歸併這部分,設定標記點,i = lo 和 j = mid+1 迴圈的條件應該是
1如果後面前面這組數的第i個數大於後面某個數,count就加mid-i+1。當然歸併時需要乙個臨時陣列來儲存這些改變位置的數,int i =lo;
2int j = mid + 1;3
while(i <= mid&&j <=hi)
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這裡帶給我最大的收穫就是count是全域性變數,因此才可以在不斷的遞迴中一直累加,我原先的想法就是在遞迴中看能不能返回逆序對的個數,或者在引數中間加入逆序對的個數一直傳遞。這次終於得到了解答,還有這個歸併時他建立的臨時陣列也很巧妙,最終又賦值給原陣列。最棒的就是遞迴這部分,以前老是理不清,想不清,看來以後得多用用遞迴。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 }
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...