前言
學了這麼久的歸併排序,感覺沒什麼卵用。。。。而且STL中有sort這麼強大的工具,歸併排序早就喫灰了
先回顧下什麼是歸併排序
要想知道什麼是歸併排序,先看看什麼是歸併
舉個例子:將兩個已經排好序(例如從小到大)的數組合併成一個有序數組,要求時間複雜度O(n)
用two-pointer的思想很好解決,用一個pointer指向第一個數組,另一個指向第二個數組,比較兩個pointer所指的元素大小,哪個小九八哪個放進結果數組中,並把剛剛的pointer往後移,,,,,很簡單就不再說了,代碼如下
基於這種想法,可以得到歸併排序的算法
一開始數組是無序的,當分割的兩個數組都只有一個元素時,即可排序。此時兩個元素的數組就有序,接着四個元素的數組就有序。。。。。依次,可以得到最終的有序數組,顯然這種排序算法的時間複雜度爲O(logN)
Coding如下:
const int MAX = 5e5+5;
int N;
int a[MAX];
void merge(int l1,int r1,int l2,int r2) {
int i=l1,j=l2;
int tmp[MAX],count=0;
while (i<=r1&&j<=r2) {
if(a[i]<=a[j]) tmp[count++]=a[i++];
else {
tmp[count++]=a[j++];
res += r1-i+1;
}
}
while (i<=r1) tmp[count++]=a[i++];
while (j<=r2) tmp[count++]=a[j++];
for (int k = 0; k < count; ++k) {
a[l1+k]=tmp[k];
}
}
void mergeSort(int l,int r){
if(l>=r) return;
int mid = (l+r)/2;
mergeSort(l,mid);
mergeSort(mid+1,r);
merge(l,mid,mid+1,r);
}
下面看應用,題目如下
題目描述
貓貓 TOM 和小老鼠 JERRY 最近又較量上了,但是畢竟都是成年人,他們已經不喜歡再玩那種你追我趕的遊戲,現在他們喜歡玩統計。
最近,TOM 老貓查閱到一個人類稱之爲“逆序對”的東西,這東西是這樣定義的:對於給定的一段正整數序列,逆序對就是序列中 ai>aj 且 i<j的有序對。知道這概念後,他們就比賽誰先算出給定的一段正整數序列中逆序對的數目。注意序列中可能有重複數字。
Update:數據已加強。
輸入格式
第一行,一個數 n,表示序列中有 n個數。
第二行 n 個數,表示給定的序列。序列中每個數字不超過 10^9。
輸出格式
輸出序列中逆序對的數目。
輸入輸出樣例
輸入 #1複製
6
5 4 2 6 3 1
輸出 #1複製
11
說明/提示
對於 25% 的數據,n≤2500
對於50% 的數據,n≤4×10^4。
對於所有數據,n≤5×10^5
分析
如果直接使用二重循環,顯然數據太大,一定會超時!!!!
歸併排序思想的應用:
拿樣例來說,最後一步歸併時元素如下
2 4 5 | 1 3 6
可以看出,逆序對的個數可以有如下規律:對於右邊的每一個元素,在左邊找到第一個比右邊大的元素的下標,每個右邊的元素可以產生
3-左邊元素的下標(從1開始)+1 個逆序對。由此可以寫出如下代碼
const int MAX = 5e5+5;
int N;
int a[MAX];
ll res;
void merge(int l1,int r1,int l2,int r2) {
int i=l1,j=l2;
int tmp[MAX],count=0;
while (i<=r1&&j<=r2) {
if(a[i]<=a[j]) tmp[count++]=a[i++];
else {
tmp[count++]=a[j++];
res += r1-i+1; //只會在這裏產生逆序對
}
}
while (i<=r1) tmp[count++]=a[i++];
while (j<=r2) tmp[count++]=a[j++];
for (int k = 0; k < count; ++k) {
a[l1+k]=tmp[k];
}
}
void mergeSort(int l,int r){
if(l>=r) return;
int mid = (l+r)/2;
mergeSort(l,mid);
mergeSort(mid+1,r);
merge(l,mid,mid+1,r);
}
加強版逆序對
題目鏈接:P1966火柴排隊
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAX = 5e5+5;
const int INF = 0x7fffffff;
const int MOD = 1e8-3;
int N,K;
int a[MAX];
ll res;
struct Point {
int preValue,index;
}pa[MAX],pb[MAX];
void merge(int l1,int r1,int l2,int r2) {
int i=l1,j=l2;
int tmp[MAX],count=0;
while (i<=r1&&j<=r2) {
if(a[i]<=a[j]) tmp[count++]=a[i++];
else {
tmp[count++]=a[j++];
res = (res+r1-i+1)%MOD;
}
}
while (i<=r1) tmp[count++]=a[i++];
while (j<=r2) tmp[count++]=a[j++];
for (int k = 0; k < count; ++k) {
a[l1+k]=tmp[k];
}
}
void mergeSort(int l,int r){
if(l>=r) return;
int mid = (l+r)/2;
mergeSort(l,mid);
mergeSort(mid+1,r);
merge(l,mid,mid+1,r);
}
bool cmp(const Point& aa,const Point& bb) {
return aa.preValue<bb.preValue;
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
#ifdef LOCAL
freopen("../data", "r", stdin);
#endif
cin >> N;
for (int i = 0; i < N; ++i) {
cin >> pa[i].preValue;
pa[i].index=i;
}
for (int i = 0; i < N; ++i) {
cin >> pb[i].preValue;
pb[i].index=i;
}
sort(pa,pa+N,cmp);
sort(pb,pb+N,cmp);
mergeSort(0,N-1);
for (int i = 0; i < N; ++i) {
a[pa[i].index]=pb[i].index;
}
mergeSort(0,N-1);
cout << res << endl;
}