归并排序是分治法的经典实现,适合大规模数据排序,尤其适合需要稳定排序的场景(如数据库排序)
cpp
#include <stdlib.h> // 用于动态内存分配
// 合并两个已排序的子数组
void merge(int arr[], int left, int mid, int right) {
int i, j, k;
int n1 = mid - left + 1; // 左子数组长度
int n2 = right - mid; // 右子数组长度
// 创建临时数组
int* L = (int*)malloc(n1 * sizeof(int));
int* R = (int*)malloc(n2 * sizeof(int));
// 复制数据到临时数组
for (i = 0; i < n1; i++) L[i] = arr[left + i];
for (j = 0; j < n2; j++) R[j] = arr[mid + 1 + j];
// 合并临时数组到原数组
i = 0; // 左数组索引
j = 0; // 右数组索引
k = left; // 合并后的数组索引
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}
// 复制剩余元素
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
free(L); // 释放临时内存
free(R);
}
// 归并排序递归函数
void mergeSort(int arr[], int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2; // 防止整数溢出
mergeSort(arr, left, mid); // 排序左半部分
mergeSort(arr, mid + 1, right); // 排序右半部分
merge(arr, left, mid, right); // 合并结果
}
}
cpp
#include <stdio.h>
// 打印数组函数
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int arr[] = {12, 11, 13, 5, 6, 7}; // 待排序数组
int n = sizeof(arr) / sizeof(arr[0]); // 计算数组长度
printf("排序前的数组: \n");
printArray(arr, n);
mergeSort(arr, 0, n - 1); // 调用归并排序
printf("排序后的数组: \n");
printArray(arr, n);
return 0;
}
优化建议
-
**避免重复内存分配:**每次合并时动态分配临时数组会带来性能损耗,可以预先分配一个全局临时数组,减少内存分配次数。
-
**小数组使用插入排序:**当子数组规模较小时(如长度 ≤ 15),插入排序效率更高,可减少递归调用开销。
-
**非递归实现(自底向上):**用循环代替递归,避免栈溢出风险,适合超大规模数据。
-
**提前检查有序性:**合并前检查左右子数组是否已有序,避免不必要的合并操作。
优化后代码:
cpp
#include <stdio.h>
#include <stdlib.h>
// 插入排序(用于小规模数据)
void insertionSort(int arr[], int left, int right) {
for (int i = left + 1; i <= right; i++) {
int key = arr[i];
int j = i - 1;
while (j >= left && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
// 合并函数(使用预分配临时数组)
void merge(int arr[], int temp[], int left, int mid, int right) {
int i = left, j = mid + 1, k = left;
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
}
}
while (i <= mid) temp[k++] = arr[i++];
while (j <= right) temp[k++] = arr[j++];
// 将临时数组复制回原数组
for (k = left; k <= right; k++) {
arr[k] = temp[k];
}
}
// 归并排序主函数(递归+优化)
void mergeSort(int arr[], int temp[], int left, int right) {
const int INSERTION_THRESHOLD = 15; // 插入排序阈值
if (right - left <= INSERTION_THRESHOLD) {
insertionSort(arr, left, right);
return;
}
int mid = left + (right - left) / 2;
mergeSort(arr, temp, left, mid);
mergeSort(arr, temp, mid + 1, right);
// 检查是否已有序
if (arr[mid] <= arr[mid + 1]) return;
merge(arr, temp, left, mid, right);
}
// 封装的归并排序入口函数
void optimizedMergeSort(int arr[], int n) {
int* temp = (int*)malloc(n * sizeof(int)); // 预分配临时数组
if (temp == NULL) {
printf("内存分配失败\n");
return;
}
mergeSort(arr, temp, 0, n - 1);
free(temp);
}
// 打印数组
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int arr[] = {12, 11, 13, 5, 6, 7, 3, 2, 8};
int n = sizeof(arr) / sizeof(arr[0]);
printf("排序前: ");
printArray(arr, n);
optimizedMergeSort(arr, n);
printf("排序后: ");
printArray(arr, n);
return 0;
}
优化说明:
优化点 | 改进效果 | 适用场景 |
---|---|---|
预分配临时数组 | 减少内存分配次数,提升性能 | 大规模数据排序 |
小数组插入排序 | 减少递归深度,降低函数调用开销 | 子数组长度 ≤ 15 |
提前检查有序性 | 避免不必要的合并操作 | 输入数据部分有序 |
入口函数封装 | 简化调用逻辑,隐藏临时数组细节 | 通用 |
进一步优化方向:
**1.自底向上非递归实现,**通过循环迭代代替递归,避免栈溢出
cpp
void bottomUpMergeSort(int arr[], int n) {
int* temp = (int*)malloc(n * sizeof(int));
for (int size = 1; size < n; size *= 2) {
for (int left = 0; left < n - size; left += 2 * size) {
int mid = left + size - 1;
int right = (left + 2 * size - 1 < n - 1) ? left + 2 * size - 1 : n - 1;
merge(arr, temp, left, mid, right);
}
}
free(temp);
}
**2.原地归并排序:**减少空间复杂度至 O(1)O(1),但实现复杂且时间效率可能降低。
优化后的归并排序在保持稳定性的前提下,显著提升了实际运行效率,尤其适合处理大规模或部分有序的数据集。