/*歸併排序是建立在歸併操作上的一種有效的排序算法。該算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱爲2-路歸併。
算法描述
把長度爲n的輸入序列分成兩個長度爲n/2的子序列;
對這兩個子序列分別採用歸併排序;
將兩個排序好的子序列合併成一個最終的排序序列。
算法分析
歸併排序是一種穩定的排序方法。和選擇排序一樣,歸併排序的性能不受輸入數據的影響,但表現比選擇排序好的多,因爲始終都是O(nlogn)的時間複雜度。代價是需要額外的內存空間。
*/
#import <Foundation/Foundation.h>
void Merge(int sourceArr[],int tempArr[], int startIndex, int midIndex, int endIndex)
{
int i = startIndex, j=midIndex+1, k = startIndex;
while(i!=midIndex+1 && j!=endIndex+1)
{
if(sourceArr[i] > sourceArr[j])
tempArr[k++] = sourceArr[j++];
else
tempArr[k++] = sourceArr[i++];
}
while(i != midIndex+1)
tempArr[k++] = sourceArr[i++];
while(j != endIndex+1)
tempArr[k++] = sourceArr[j++];
for(i=startIndex; i<=endIndex; i++)
sourceArr[i] = tempArr[i];
}
//內部使用遞歸
void MergeSort(int sourceArr[], int tempArr[], int startIndex, int endIndex)
{
int midIndex;
if(startIndex < endIndex)
{
midIndex = startIndex + (endIndex-startIndex) / 2;//避免溢出int
MergeSort(sourceArr, tempArr, startIndex, midIndex);
MergeSort(sourceArr, tempArr, midIndex+1, endIndex);
Merge(sourceArr, tempArr, startIndex, midIndex, endIndex);
}
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a[7] = {4, 2, 1, 5, 7, 6, 3};
int i, b[7];
MergeSort(a, b, 0, 6);
for(i=0; i<7; i++)
printf("%d ", a[i]);
printf("\n");
}
return 0;
}
速度僅次於快速排序,爲穩定排序算法,一般用於對總體無序,但是各子項相對有序的數列,應用見2011年普及複賽第3題“瑞士輪”的標程
歸併排序比較佔用內存,但卻是一種效率高且穩定的算法。
改進歸併排序在歸併時先判斷前段序列的最大值與後段序列最小值的關係再確定是否進行復制比較。如果前段序列的最大值小於等於後段序列最小值,則說明序列可以直接形成一段有序序列不需要再歸併,反之則需要。所以在序列本身有序的情況下時間複雜度可以降至O(n)
TimSort可以說是歸併排序的終極優化版本,主要思想就是檢測序列中的天然有序子段(若檢測到嚴格降序子段則翻轉序列爲升序子段)。在最好情況下無論升序還是降序都可以使時間複雜度降至爲O(n),具有很強的自適應性。
最好時間複雜度 | 最壞時間複雜度 | 平均時間複雜度 | 空間複雜度 | 穩定性 | |
傳統歸併排序 | O(nlogn) | O(nlogn) | O(nlogn) | T(n) | 穩定 |
改進歸併排序 [1] | O(n) | O(nlogn) | O(nlogn) | T(n) | 穩定 |
TimSort [2] | O(n) | O(nlogn) | O(nlogn) | T(n) | 穩定 |
注:文獻 [1] 是一種改進的原地歸併算法,空間複雜度爲O(1)。在表格裏的改進歸併排序只是引入其預先判斷的這一步,這樣便可使傳統歸併排序時間複雜度降至O(n)。(這段來之百度百科)