自然合併排序是合併排序算法的一種改進, 對於初始給定的數組, 通常存在多個長度大於1的已自然排好序的子數組段.
例如, 若數組a中元素爲{1, 5, 2, 3, 6, 0, 7, 4, 8}, 則自然排好序的子數組段有{1, 5}, {2, 3, 6}, {0, 7}, {4, 8}. 用一次對數組a的線性掃描就足以找出所有這些排好序的子數組段. 然後將相鄰的排好序的子數組段兩兩合併,構成更大的排好序的子數組段({1, 2, 3, 5, 6}, {0, 4, 7, 8}).繼續合併相鄰排好序的子數組段,直至整個數組已排好序.
通常情況下, 按此方式進行合併排序所需的合併次數較少. 例如, 對於所給的n元素數組已排好序的極端情況, 自然合併排序算法不需要執行合併.
#include <iostream>
using namespace std;
typedef int Type;
void print(Type a[], int n)
{
for (int i=0; i<n; ++i) {
cout << a[i] << " ";
}
cout << endl;
}
void GetIndex(int a[], int t[], int n, int &m)
{
// 記錄自然分組後每個子串的起始座標
int j = 0;
t[j++] = 0;
for(int i = 0; i < n-1; i++) {
if(a[i+1] < a[i]) {
t[j++] = i+1;
}
}
m = j; // m爲自然分組的個數
}
void Merge(Type c[], Type d[], int l, int m, int r)
{
// 合併c[l:m]和c[m+1:r]到d[1:r]
int i = l, j = m + 1, k = l;
while (i <= m && j <= r) {
if (c[i] <= c[j]) {
d[k++] = c[i++];
} else {
d[k++] = c[j++];
}
}
if (i > m) {
for (int q = j; q <= r; q++) {
d[k++] = c[q];
}
} else {
for (int q = i; q <= m; q++) {
d[k++] = c[q];
}
}
}
void MergePass(Type x[], Type y[], Type t[], int s, int n, int cnt)
{
// 合併自然分組後相鄰的子數組
int i = 0;
while(i <= cnt - 2 * s) {
// 當自然分組後的子數組個數爲偶數時, r = n, 表示恰好兩兩合併
int r = ((i + 2 * s) < cnt) ? t[i+2*s] : n;
Merge(x, y, t[i], t[i+s]-1, r-1);
i = i + 2 * s;
}
if(i + s < cnt) {
Merge(x, y, t[i], t[i+s]-1, n-1);
} else if (i < cnt) {
// 剩餘元素直接複製
for(int j = t[i]; j <= n - 1; j++) {
y[j] = x[j];
}
}
}
void MergeSort(Type a[], Type t[], int n, int cnt)
{
Type *b = new Type[n];
int s = 1;
while (s < cnt) {
MergePass(a, b, t, s, n, cnt); // 合併到數組b
s += s;
MergePass(b, a, t, s, n, cnt); // 合併到數組a
s += s;
}
delete[] b;
}
int main(int argc, char *argv[])
{
Type a[] = {1, 5, 2, 3, 6, 0, 7, 4, 8};
int len = sizeof(a) / sizeof(int);
Type *t = new Type[len];
int cnt; // 記錄自然分組個數
GetIndex(a, t, len, cnt);
MergeSort(a, t, len, cnt);
print(a, len);
delete[] t;
return 0;
}