我们知道归并排序是将一个无序的数组两两划分,最终划分成每个组内有序的子元素,组后再将若干个组内有序的元素合并成一个完整有序的数组。这个思路可以使用递归和非递归算法来实现,我在此主要讲自然合并排序
自然合并排序当然也是合并排序,所谓的自然只不过就是指:所划分的子数组不在是两两划分,而是每一个子数组都是已经有序的,且每个子数组的长度不在确定。这就是自然合并排序与合并排序的区别。
如数组
{1,5, 3, 6, 7, 2,4}
将此数组可以自然划分为三个有序的子数组
{1,5} {3,6,7} {2,4}
由于自然划分的不确定性,所以此算法无法由递归实现,具体代码如下:
#include <stdio.h>
// 记录初始自然排好序的子数组段的起始下标及个数
int getIndex(int *list, int *index, int n) {
int count = 0;
index[count++] = 0;
for (int i = 0; i < n-1; i++) {
if (list[i+1] < list[i]) {
index[count++] = i+1;
}
}
return count;
}
//将一个数组内的两个子数组合并
// left是第一个有序子数组段的起始下标
// middle是第一个有序子数组段的末尾下标
// right是第二个有序子数组段的末尾下标
void mergeSublist(int *list_1, int *list_2, int left, int middle, int right) {
int i = left;
int j = middle+1; // j为第二个的起始下标
int k = left;
while (i <= middle && j <= right) {
// 每次取两段中较小的一个
if (list_1[i] <= list_1[j]) {
list_2[k++] = list_1[i++];
}else
{
list_2[k++] = list_1[j++];
}
}
// 合并两段中的剩余部分
while (i <= middle) {
list_2[k++] = list_1[i++];
}
while (j <= right) {
list_2[k++] = list_1[j++];
}
}
//合并过程函数
//将组内有序的数组合并,具体合并由Merge()实现
/*
每合次s增加,就合并一段子数组
每个子数组的起始位置记录在t数组中
index[i] 表示要合并第一个子数组的起始元素在原数组的下标 即每次合并的left
index[i+s]-1 表示要合并的第一个子数组的最后一个元素在原数组的下标 即每次合并的middle
index[i+2*s]-1 表示每次要合并的第二个子数组的最后一个元素 即每次合并的right
*/
void mergePass(int *list_1, int *list_2, int *index, int s, int count, int n) {
int i = 0;
//当前数组内至少有三个子数组
while (i < count - 2*s) {
mergeSublist(list_1, list_2, index[i], index[i+s]-1, index[i+2*s]-1);
i = i + 2*s;
}
//当前数组内含有两个子数组段
if (i + s < count) {
mergeSublist(list_1, list_2, index[i], index[i+s]-1, n-1);
}
//当前数组内只有一个子数组段
else if (i < count) {
for (int j = index[i]; j <= n -1; j++) {
list_2[j] = list_1[j];
}
}
}
void mergeSort(int *list_1, int *index, int count, int n) {
int list_2[n];
int s = 1;
while (s < count) {
mergePass(list_1, list_2, index, s, count, n);
s += s;
mergePass(list_2, list_1, index, s, count, n);
s += s;
}
}
int main(void) {
int list[100], index[100];
int n;
scanf("%d", &n);
for(int i = 0; i < n; i++) {
scanf("%d", &list[i]);
}
int count = getIndex(list, index, n);
/*//输出标记数组
for (int i = 0; i < count; i++) {
printf("%d ", index[i]);
}
printf("\n");
*/
mergeSort(list, index, count, n);
for (int i = 0; i < n; i++) {
printf("%d ", list[i]);
}
return 0;
}
测试结果
输入:
7
1 5 3 6 7 2 4
输出:
1 2 3 4 5 6 7
如果代码运行出现错误,请在评论区回复作者,不胜感激!