1. 归并排序
1.1 原理介绍
归并排序的基本原理是将一个未排序的数组分解为较小的子数组,然后递归地对这些子数组进行排序,最后再将排好序的子数组合并成一个有序数组。其核心操作是将一维数组中前后相邻的两个有序序列归并为一个有序序列。
其主要步骤包括:
- 分割:将原始数组分割为较小的子数组,直到每个子数组只包含一个元素。
- 合并:逐个比较两个子数组的元素,并按顺序合并它们。
1.2 主要步骤
详细步骤:
- 分解:将待排序数组分解成两个大致相等的子数组,直到子数组中只有一个元素为止。这一步是通过递归实现的。
- 合并:将两个已排序的子数组合并成一个有序数组。在合并过程中,创建一个临时数组,比较两个子数组的元素,将较小的元素依次放入临时数组中。
- 递归合并:重复步骤 2,直到所有子数组都合并成一个完整的有序数组。
下面是一个示例来说明归并排序的详细步骤:
- 假设待排序数组为 [38, 27, 43, 3, 9, 82, 10]。
- 初始状态:原始数组为 [38, 27, 43, 3, 9, 82, 10]。
- 第一次分解:将数组分解为 [38, 27, 43, 3] 和 [9, 82, 10] 两个子数组。
- 对左边子数组进行排序:分解左边子数组 [38, 27, 43, 3],分解为 [38, 27] 和 [43, 3];继续分解为 [38] 和 [27],以及 [43] 和 [3]。 对每个子数组进行合并排序,得到 [27, 38] 和 [3, 43]。最终合并左边子数组得到 [3, 27, 38, 43]。
- 对右边子数组进行排序:分解右边子数组 [9, 82, 10],分解为 [9] 和 [82, 10]。继续分解为 [82] 和 [10]。对每个子数组进行合并排序,得到 [10, 82]。最终合并右边子数组得到 [9, 10, 82]。
- 合并左右子数组:将左边子数组 [3, 27, 38, 43] 和右边子数组 [9, 10, 82] 合并,依次比较两个子数组的元素,将较小的元素放入临时数组中。最终得到合并后的有序数组 [3, 9, 10, 27, 38, 43, 82]。
- 最终结果:经过以上步骤,我们得到了完整的有序数组 [3, 9, 10, 27, 38, 43, 82]。
1.3 代码示例
void _MergeSort(int* a, int begin, int end, int* tmp)
{
if (begin >= end)
return;
int mid = (begin + end) / 2;
//递归排序左右两部分
_MergeSort(a, begin, mid, tmp);
_MergeSort(a, mid + 1, end, tmp);
//合并两个有序数组
int begin1 = begin, end1 = mid;
int begin2 = mid + 1, end2 = end;
int i = begin;
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
{
tmp[i++] = a[begin1++];
}
else
{
tmp[i++] = a[begin2++];
}
}
//复制剩余元素
while (begin1 <= end1)
{
tmp[i++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[i++] = a[begin2++];
}
//合并临时数组到原数组
memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin) + 1);
}
//归并排序
void MergeSort(int* a, int n)
{
//创建临时空间
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == NULL)
{
perror("malloc fail");
return;
}
_MergeSort(a, 0, n - 1, tmp);
//手动释放临时空间
free(tmp);
}
//测试
int main()
{
int a[] = { 2,4,6,1,5,3,9,7,0,8 };
int n = sizeof(a) / sizeof(int);
printf("原数组为:");
for (int i = 0; i < n; i++)
{
printf("%d ", a[i]);
}
MergeSort(a, n);
printf("\n排序后数组为:");
for (int i = 0; i < n; i++)
{
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
1.4 特性总结
- 稳定性:归并排序是一种稳定的排序算法,相同元素的相对位置在排序前后不会改变。
- 时间复杂度:归并排序的时间复杂度为 O(N*log N),其中 N 是待排序数组的长度。
- 空间复杂度:归并排序的空间复杂度为 O(N),需要额外的空间来存储临时数组。
- 适用性:归并排序适用于各种数据规模的排序,且在处理大规模数据时表现良好。