自然合併排序算法是對合並排序算法的一種改進。設 a[0:n-1]是無序數組,用一次對數組a的掃描可以找出其中 自然排好序的子數組,然後將相鄰的排好序的子數組段兩 兩合併,繼續合併相鄰排好序的子數組段,直至將整個數 組排好序。
code:
#include <algorithm>
#include <iostream>
using namespace std;
int getIndex(int *a, int *t, int n)
{
// 記錄初始自然排好序的子數組段的起始下標及個數
int cnt = 0;
t[cnt++] = 0; // 第一個下標爲0
for(int i = 0; i < n-1; i++)
{
if(a[i+1] < a[i])
t[cnt++] = i+1;
}
return cnt;
}
// 合併a中有序的兩段子數組到b對應的位置
// l是第一個有序子數組段的起始下標
// m是第一個有序子數組段的末尾下標
// r是第二個有序子數組段的末尾下標
void merge(int *a, int *b, int left, int mid, int right)
{
int i = left;
int j = mid+1; // j爲第二個的起始下標
int k = left;
while(i <= mid && j <= right) // 合併兩段
{
if(a[i] <= a[j]) // 每次取兩段中較小的一個
b[k++] = a[i++];
else
b[k++] = a[j++];
}
// 合併兩段中的剩餘部分
while(i <= mid) b[k++] = a[i++];
while(j <= right) b[k++] = a[j++];
}
void mergePass(int *a, int *b, int *t, int s, int n, int cnt)
{
int i = 0;
while(i <= cnt - 2*s) // 用子段數組個數控制循環
{
if(i + 2*s < cnt)
merge(a, b, t[i], t[i+s] - 1, t[i + 2*s] -1);
else
merge(a, b, t[i], t[i+s] - 1, n -1);
i = i + 2*s;
}
// 剩餘未合併的不足s
if(i + s < cnt)
merge(a, b, t[i], t[i+s] - 1, n-1);
else if(i < cnt)
for(int j = t[i]; j <= n-1; j++)
b[j] = a[j];
}
void mergeSort(int *a, int *t, int n, int cnt)
{
// s指的是每次合併的子段數或者說步長
// s = 1時是是對於初始分段1-1合併
// s = 2時是相對與初始分段2-2合併,2是之前合併好的1-1
int b[100];
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;
}
}
int main()
{
int n;
int a[100], t[100];
cin >> n;
for(int i = 0; i < n; i++)
cin >> a[i];
int cnt = getIndex(a, t, n);
mergeSort(a, t, n, cnt);
for(int i = 0; i < n; i++)
cout << a[i] << ' ';
return 0;
}