归并排序是分治法的经典实现,适合大规模数据排序,尤其适合需要稳定排序的场景(如数据库排序)
#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); // 合并结果}
}
#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;
}
优化建议
1. 避免重复内存分配:每次合并时动态分配临时数组会带来性能损耗,可以预先分配一个全局临时数组,减少内存分配次数。
2. 小数组使用插入排序:当子数组规模较小时(如长度 ≤ 15),插入排序效率更高,可减少递归调用开销。
3. 非递归实现(自底向上):用循环代替递归,避免栈溢出风险,适合超大规模数据。
4. 提前检查有序性:合并前检查左右子数组是否已有序,避免不必要的合并操作。
优化后代码:
#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.自底向上非递归实现,通过循环迭代代替递归,避免栈溢出
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),但实现复杂且时间效率可能降低。
优化后的归并排序在保持稳定性的前提下,显著提升了实际运行效率,尤其适合处理大规模或部分有序的数据集。