Files
cAI/cKit/Sort/c_InPlaceMSDRadixSort.c
T
2026-08-10 01:21:15 +08:00

106 lines
4.1 KiB
C

#include <c_InPlaceMSDRadixSort.h>
#include <c_Memory.h>
/**
* Inline helper to safely extract a character at string offset d.
* Automatically maps a string's null terminator to a sentinel value of -1.
*/
C_STATIC_FORCE_INLINE
int c_InPlaceMSDRadixSort_CharAt(const char* str, c_size_t d) {
if (str == NULL) return -1;
c_size_t i = 0;
while (i < d && str[i] != '\0') {
i++;
}
if (str[i] == '\0' || i < d) return -1;
return (unsigned char)str[i];
}
/**
* Core Private Recursive Sub-partition In-place Sorting Subroutine.
* Employs a localized head/tail lookup permutation ring to operate directly within array slices.
*/
static void c_InPlaceMSDRadixSort_Recursive(char** arr, long long lo, long long hi, c_size_t d,
c_size_t R, c_size_t* count_buf, long long* heads, long long* tails) {
if (hi <= lo) return;
// Elements are shifted forward by +2 slots to absorb the -1 string end sentinel gracefully
c_size_t total_buckets = R + 2;
memset(count_buf, 0, total_buckets * sizeof(c_size_t));
// Pass A: Compute frequency counts for the current digit slice
for (long long i = lo; i <= hi; i++) {
int c = c_InPlaceMSDRadixSort_CharAt(arr[i], d);
count_buf[c + 2]++;
}
// Pass B: Transform frequencies into absolute head and tail cursor index maps
heads[0] = lo;
tails[0] = lo + (long long)count_buf[0];
for (c_size_t r = 1; r < total_buckets; r++) {
heads[r] = tails[r - 1];
tails[r] = heads[r] + (long long)count_buf[r];
}
// Pass C: Cyclic Permutation Swap Element Loop (In-Place Distribution)
for (c_size_t r = 0; r < total_buckets; r++) {
while (heads[r] < tails[r]) {
long long curr_idx = heads[r];
int c = c_InPlaceMSDRadixSort_CharAt(arr[curr_idx], d);
c_size_t bucket = (c_size_t)(c + 2);
if (bucket == r) {
heads[r]++; // Element is already in its correct bucket, step forward
} else {
// Evict the element to its correct destination bucket via data swap
long long dest_idx = heads[bucket];
char* temp = arr[curr_idx];
arr[curr_idx] = arr[dest_idx];
arr[dest_idx] = temp;
heads[bucket]++; // Increment the destination bucket's cursor
}
}
}
// Pass D: Recursively process sub-arrays for each character bucket
// Shorter strings that terminated (sentinel character index 0) do not need deeper processing
long long current_lo = lo + (long long)count_buf[0];
for (c_size_t r = 1; r < total_buckets; r++) {
long long current_hi = current_lo + (long long)count_buf[r] - 1;
if (current_hi > current_lo) {
c_InPlaceMSDRadixSort_Recursive(arr, current_lo, current_hi, d + 1, R, count_buf, heads, tails);
}
current_lo = current_hi + 1;
}
}
/**
* Sorts an array of variable-length strings completely in-place.
* Space Complexity: O(1) Auxiliary Heap memory footprint (Excluding recursive tracking arrays bound to R)
*/
c_err_t c_InPlaceMSDRadixSort_Sort(const c_InPlaceMSDRadixSort_t* sort, char** arr, c_size_t n) {
if (sort == NULL || arr == NULL || sort->R == 0) return C_ERR_PARAM;
if (n <= 1) return C_ERR_OK;
// Radix-bound tracking buffers are allocated once upfront to eliminate heap overhead in hot loops
c_size_t total_buckets = sort->R + 2;
c_size_t* count_buf = (c_size_t*)C_ALLOC(total_buckets * sizeof(c_size_t));
long long* heads = (long long*)C_ALLOC(total_buckets * sizeof(long long));
long long* tails = (long long*)C_ALLOC(total_buckets * sizeof(long long));
if (count_buf == NULL || heads == NULL || tails == NULL) {
C_FREE(count_buf); C_FREE(heads); C_FREE(tails);
return C_ERR_NOMEM;
}
// Launch the in-place cyclic permutation partition tree
c_InPlaceMSDRadixSort_Recursive(arr, 0, (long long)n - 1, 0, sort->R, count_buf, heads, tails);
C_FREE(count_buf);
C_FREE(heads);
C_FREE(tails);
return C_ERR_OK;
}