Files
cKit/Sort/c_MergeSort.c
2026-08-30 22:24:45 +08:00

331 lines
12 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <c_MergeSort.h>
#include <stdlib.h>
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
/**
* @brief 自顶向下归并排序核心合并内部函数(完全规避 c_size_t 下溢)
*
* 采用左闭右开区间映射,或精准的边界条件限制,确保所有的索引自加和自减都不触碰回绕风险。
*/
static void c_MergeInternal(char* array_base, c_size_t low, c_size_t mid, c_size_t high,
char* aux_buf, c_size_t size, c_SortCompare_t cmp, void* args)
{
// 将待合并的两个有序子区间 [low, mid] 和 [mid + 1, high] 的数据完整复制到辅助缓冲区
// 拷贝的总数据量为从 low 到 high(含)的闭区间元素总和
c_size_t total_elements = high - low + 1;
memcpy(aux_buf + (low * size), array_base + (low * size), total_elements * size);
c_size_t i = low; // 左半边子区间的游标起始点
c_size_t j = mid + 1; // 右半边子区间的游标起始点
// 双指针滑窗合并回原数组
for (c_size_t k = low; k <= high; k++) {
if (i > mid) {
// 左半边已经全部用尽,直接无脑拷贝右半边剩余元素
memcpy(array_base + (k * size), aux_buf + (j * size), size);
j++;
}
else if (j > high) {
// 右半边已经全部用尽,直接无脑拷贝左半边剩余元素
memcpy(array_base + (k * size), aux_buf + (i * size), size);
i++;
}
else {
// 核心双指针比对
char* elem_i = aux_buf + (i * size);
char* elem_j = aux_buf + (j * size);
// 使用 <= 0 判定。如果左边的值小于或等于右边,优先选用左边。
// 这正是归并排序能够完美保留相同元素原始先后相对位置(保持 Stable)的关键所在!
if (cmp(elem_i, elem_j, args) <= 0) {
memcpy(array_base + (k * size), elem_i, size);
i++;
} else {
memcpy(array_base + (k * size), elem_j, size);
j++;
}
}
}
}
/**
* @brief 递归驱动函数
*/
static void c_MergeSortRecursive(char* array_base, c_size_t low, c_size_t high,
char* aux_buf, c_size_t size, c_SortCompare_t cmp, void* args)
{
// 终止递归条件:当区间收缩到只包含 1 个元素时自然返回
if (low >= high) {
return;
}
// 防整数溢出的中间索引计算法
c_size_t mid = low + ((high - low) >> 1);
// 分治法阶段 1:左半区递归
c_MergeSortRecursive(array_base, low, mid, aux_buf, size, cmp, args);
// 分治法阶段 2:右半区递归(由于 mid + 1 永远大于 mid,不可能在此处发生无符号数反向绕回)
c_MergeSortRecursive(array_base, mid + 1, high, aux_buf, size, cmp, args);
// 分治法阶段 3:就地执行双向有序序列的高效合并
c_MergeInternal(array_base, low, mid, high, aux_buf, size, cmp, args);
}
/**
* @brief 工业级泛型自顶向下归并排序入口
*
* @param base 指向待排序连续数组首元素的指针
* @param num 数组中元素的总个数
* @param size 每个元素所占用的内存字节大小 (sizeof)
* @param cmp 带自定义上下文参数的比对回调函数指针 (不能为 NULL)
* @param args 传递给比对回调函数的自定义上下文参数指针
*/
void c_MergeSort(void* base, c_size_t num, c_size_t size, c_SortCompare_t cmp, void* args) {
if (!base || num < 2 || size == 0 || !cmp) {
return;
}
char* array_base = (char*)base;
// 归并排序无法实现真正的泛型就地常数级存储,必须在堆或外部开辟一块大小为 N 的临时辅助阴影缓冲区。
// 这里采用标准 malloc 动态分配。如果在嵌入式环境中,可以将其修改为用户传入或静态内存池分配。
char* aux_buf = (char*)malloc(num * size);
if (!aux_buf) {
return; // 分配失败防御
}
// 触发递归控制流:闭区间范围为 0 到 num - 1
// 由于 num >= 2num - 1 经过了前置拦截,绝对不会引发 0 - 1 的无符号数最大值回绕灾难
c_MergeSortRecursive(array_base, 0, num - 1, aux_buf, size, cmp, args);
// 严密清理辅助缓冲区,斩断内存泄漏
free(aux_buf);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
/**
* @brief 内部辅助内联函数:获取两个无符号数中的较小值(防止宏重复计算)
*/
C_STATIC_FORCE_INLINE
c_size_t c_InternalMin(c_size_t a, c_size_t b) {
return (a < b) ? a : b;
}
/**
* @brief 归并排序核心合并内部函数(复用双指针滑窗逻辑)
*/
C_STATIC_FORCE_INLINE
void c_BU_MergeInternal(char* array_base, c_size_t low, c_size_t mid, c_size_t high,
char* aux_buf, c_size_t size, c_SortCompare_t cmp, void* args){
c_size_t total_elements = high - low + 1;
memcpy(aux_buf + (low * size), array_base + (low * size), total_elements * size);
c_size_t i = low;
c_size_t j = mid + 1;
for (c_size_t k = low; k <= high; k++) {
if (i > mid) {
memcpy(array_base + (k * size), aux_buf + (j * size), size);
j++;
}
else if (j > high) {
memcpy(array_base + (k * size), aux_buf + (i * size), size);
i++;
}
else {
char* elem_i = aux_buf + (i * size);
char* elem_j = aux_buf + (j * size);
if (cmp(elem_i, elem_j, args) <= 0) {
memcpy(array_base + (k * size), elem_i, size);
i++;
} else {
memcpy(array_base + (k * size), elem_j, size);
j++;
}
}
}
}
/**
* @brief 工业级泛型自底向上归并排序(纯迭代非递归,完全规避栈溢出风险)
*
* @param base 指向待排序连续数组首元素的指针
* @param num 数组中元素的总个数
* @param size 每个元素所占用的内存字节大小 (sizeof)
* @param cmp 带自定义上下文参数的比对回调函数指针 (不能为 NULL)
* @param args 传递给比对回调函数的自定义上下文参数指针
*/
void c_MergeSort_BottomUp(void* base, c_size_t num, c_size_t size, c_SortCompare_t cmp, void* args) {
if (!base || num < 2 || size == 0 || !cmp) {
return;
}
char* array_base = (char*)base;
// 分配 N 大小的临时阴影缓冲区
char* aux_buf = (char*)malloc(num * size);
if (!aux_buf) {
return;
}
// 外层循环:控制当前合并的子序列步长 sz (以 1, 2, 4, 8, ... 指数级翻倍递增)
// 限制条件:sz < num,由于通过前置校验限制 num >= 2,因此循环至少执行一轮
for (c_size_t sz = 1; sz < num; sz = sz + sz) {
// 内层循环:按当前子序列步长,成对进行两个子区间的有序合并
// 控制条件:low < num - sz。通过逆向加法或前置限制,确保 (num - sz) 不会发生无符号下溢
c_size_t limit = num - sz;
for (c_size_t low = 0; low < limit; low += sz + sz) {
// 计算当前左半边有序区间的终点 mid
c_size_t mid = low + sz - 1;
// 计算当前右半边有序区间的终点 high。
// 核心安全优化:若右半区不满足整步长大小,直接利用 c_InternalMin 将其安全截断到真实的最后一位 (num - 1)
// 由于通过前置校验,num - 1 绝对不会产生无符号回绕
c_size_t high = c_InternalMin(low + sz + sz - 1, num - 1);
// 就地执行数据合并
c_BU_MergeInternal(array_base, low, mid, high, aux_buf, size, cmp, args);
}
}
// 清理堆空间
free(aux_buf);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
// 设置小数组自适应截断阈值,通常在 12 - 16 之间性能达到极值
#define COMPONENT_MERGE_CUTOFF 15
/**
* @brief 局部内联优化的泛型插入排序(供归并排序降级使用,规避下溢风险)
*/
C_STATIC_FORCE_INLINE
void c_Internal_InsertionSort(char* base, c_size_t low, c_size_t high, c_size_t size, c_SortCompare_t cmp, void* args) {
for (c_size_t i = low + 1; i <= high; i++) {
c_size_t j = i;
char* item_i = base + (i * size);
// 栈上开辟单元素缓冲区,实现半交换单向平移优化
char v_buf[256];
memcpy(v_buf, item_i, size);
while (j > low) {
char* current = base + (j * size);
char* previous = base + ((j - 1) * size);
if (cmp(v_buf, previous, args) < 0) {
memcpy(current, previous, size);
j--;
} else {
break;
}
}
memcpy(base + (j * size), v_buf, size);
}
}
/**
* @brief 核心合并逻辑:直接从 src 合并到 dst,消除往返拷贝开销
*/
C_STATIC_FORCE_INLINE
void c_OptMergeInternal(char* src, char* dst, c_size_t low, c_size_t mid, c_size_t high,
c_size_t size, c_SortCompare_t cmp, void* args)
{
c_size_t i = low;
c_size_t j = mid + 1;
for (c_size_t k = low; k <= high; k++) {
if (i > mid) {
memcpy(dst + (k * size), src + (j * size), size);
j++;
}
else if (j > high) {
memcpy(dst + (k * size), src + (i * size), size);
i++;
}
else {
char* elem_i = src + (i * size);
char* elem_j = src + (j * size);
if (cmp(elem_i, elem_j, args) <= 0) {
memcpy(dst + (k * size), elem_i, size);
i++;
} else {
memcpy(dst + (k * size), elem_j, size);
j++;
}
}
}
}
/**
* @brief 优化版交替控制流递归核心
*
* 注意:这里的 src 和 dst 在每一层递归中都会发生调换,使得合并直接把数据推向正确的上一层载体。
*/
static void c_OptMergeRecursive(char* src, char* dst, c_size_t low, c_size_t high,
c_size_t size, c_SortCompare_t cmp, void* args)
{
// 优化点 1:小数组截断,当区间长度小于等于阈值时直接调用插入排序
if (high - low < COMPONENT_MERGE_CUTOFF) {
c_Internal_InsertionSort(dst, low, high, size, cmp, args);
return;
}
c_size_t mid = low + ((high - low) >> 1);
// 优化点 3:通过互换 src 与 dst 指针角色,使得子层计算出来的有序序列直接存储在 src 中
c_OptMergeRecursive(dst, src, low, mid, size, cmp, args);
c_OptMergeRecursive(dst, src, mid + 1, high, size, cmp, args);
// 优化点 2:有序性前置检查。
// 如果左半区间的最大元素已经小于或等于右半区的最小元素,说明当前整体已经完全有序
// 此时只需直接从 src 拷贝到 dst,完全免去滑窗合并在指令和缓存上的无效消耗
char* left_max = src + (mid * size);
char* right_min = src + ((mid + 1) * size);
if (cmp(left_max, right_min, args) <= 0) {
memcpy(dst + (low * size), src + (low * size), (high - low + 1) * size);
return;
}
// 正常合并:将两个子序列安全推入 dst 目标
c_OptMergeInternal(src, dst, low, mid, high, size, cmp, args);
}
/**
* @brief 工业级泛型优化版归并排序(哨兵阻断、小数组截断、零往返拷贝)
*/
void c_MergeSort_Optimized(void* base, c_size_t num, c_size_t size, c_SortCompare_t cmp, void* args) {
if (!base || num < 2 || size == 0 || !cmp) {
return;
}
char* array_base = (char*)base;
// 分配 N 大小的辅助阴影缓冲区
char* aux_buf = (char*)malloc(num * size);
if (!aux_buf) {
return;
}
// 初始化时将原始数据完整同步到辅助缓冲区中,以支持首次角色分调
memcpy(aux_buf, array_base, num * size);
// 触发安全递归:此时传入的两个缓冲区分别为 aux_buf 和 array_base。
// 最终排好序的元素会完美收拢落回到原数组 array_base 中。
c_OptMergeRecursive(aux_buf, array_base, 0, num - 1, size, cmp, args);
free(aux_buf);
}