紀念下AC的歸併排序類型題……大佬勿噴,歡迎指出錯誤。
歸併排序是最近學會的一類排序方法(感謝“沒有夢想__何必遠方“),O(nlog2n)的複雜度,雖然和algorithm中的sort複雜度一樣,但在做(裝)題(逼)中也是經常用到的,比如我將會在本週內發佈的另一題解——codevs1688求逆序對中就會有很大用處。
這道題目雖然是一道水題,但用來練習歸併排序也是一道非常好的題目,因此,下面的講解並非最短題解,而是對歸併排序的講解,請注意!~~~
首先,歸併排序的主要思想是——
將兩個或兩個以上的有序表組合成一個新有序表。
這也就決定了,我們將使用遞歸的方法來完成這一排序。
那麼,既然已經知道了大體思路,就應該講講怎麼實現。
(先盜用一張圖)
大概從圖片中可以看出,歸併排序就是一次次的二分,然後再找到葉子節點後就開始兩兩比較,將小的存入臨時數組,當遍歷(O(n))完成後,就將臨時數組複製到原數組中,這樣,原數組就可以完成第一次的歸併,然後在回溯中(O(logn))完成剩餘排序,是一種十分巧妙的排序方法。
首先是遞歸的程序:
void MergeSort(int st,int en)
{
int mid=(st+en)/2;
if(st<en)
{
MergeSort(st,mid);//遍歷左子樹
MergeSort(mid+1,en);//遍歷右子樹
Merge(st,mid,en);//對當前這一段進行歸併
}
}
沒什麼可講的,對吧……
然後就是最關鍵的部分了,如何完成排序操作?
我的思路大概是:首先二分目前的數組變成兩個部分,分別各用一個指針指向兩段數組的最前端,然後比較大小,小的存入臨時數組,大的就留下繼續比較。
由於任何一個指針都需要小於該段的長度,但如果該段並未遍歷完怎麼辦?那麼,我們就直接將剩下的一堆直接插到臨時數組的最後,留給下一次的遍歷去比較。
上代碼吧:
void Merge(int st,int mid,int en)
{
int i=st;//左子樹首指針
int j=mid+1;//右子樹首指針
int k=0;
int t[100010];//臨時數組,存儲排好序的數列
while(i<=mid&&j<=en)
{
if(m[i]<m[j])
{
t[++k]=m[i];
i++;
}
else
{
t[++k]=m[j];
j++;
}
}
//第一次歸併,比較大小
while(i<=mid)
{
t[++k]=m[i];
i++;
}
while(j<=en)
{
t[++k]=m[j];
j++;
}
//第二次歸併,直接將剩下的插到隊末
for(int i=1;i<=k;i++)
m[st+i-1]=t[i];
//記得將原數組賦上排好序後的值
}
對了,整段代碼中最容易出錯的是:
for(int i=1;i<=k;i++)
m[st+i-1]=t[i];
爲什麼這樣說呢,因爲我錯……哦不,因爲這一段中臨時數組是從1(0也可以,隨便你)開始存的,而我們的原數組的賦值應該從該段數組的開頭,也就是st開始,最後還要注意是st+i-1哦!
好的,最後就上一下這道水題的題解吧(註釋請看上面):
#include <iostream>
#include <cstdio>
using namespace std;
int m[100010];
void Merge(int st,int mid,int en)
{
int i=st;
int j=mid+1;
int k=0;
int t[100010];
while(i<=mid&&j<=en)
{
if(m[i]<m[j])
{
t[++k]=m[i];
i++;
}
else
{
t[++k]=m[j];
j++;
}
}
while(i<=mid)
{
t[++k]=m[i];
i++;
}
while(j<=en)
{
t[++k]=m[j];
j++;
}
for(int i=1;i<=k;i++)
m[st+i-1]=t[i];
}
void MergeSort(int st,int en)
{
int mid=(st+en)/2;
if(st<en)
{
MergeSort(st,mid);
MergeSort(mid+1,en);
Merge(st,mid,en);
}
}
int main()
{
int n;
cin >> n;
for(int i=1;i<=n;i++)
cin >> m[i];
MergeSort(1,n);
for(int i=1;i<=n;i++)
cout << m[i] << " ";
cout << endl;
return 0;
}
額,如果說我把這道題做複雜了,那我也無言以對,只是在做1688逆序對之前做個鋪墊吧。
最後,歡迎大家繼續關注我接下來的題解,我將在這周內完成codevs1688求逆序對的題解。
ps:由於作者是一名資深蒟蒻,有錯誤也是不可避免的事,所以歡迎大家指出錯誤,我也會改正的,謝謝。