文章目录
原理
基数排序是一种非比较型整数排序算法,通过按位分配数字到桶中实现排序。其核心思想是"分配-收集"过程,分为两种策略:
LSD(最低位优先):从最低位开始排序,逐位向高位推进
MSD(最高位优先):从最高位开始,可实现不稳定排序(支持链式排序)
基数排序一般使用链表完成,这样子不需要太多额外的空间。并且链表在数据的增、删、移动上具有优势。
C语言算法实现(LSD版本)
方法1(较为简单):
1、位数确定
通过遍历数组找到最大值,确定需要处理的位数
示例:最大值802需要3位(个、十、百位)
2、计数排序子程序
统计阶段:计算当前位(如个位)各数字(0-9)出现次数
累计分布:将计数数组转换为位置索引
反向填充:保证排序稳定性(相同数字保持原顺序)
3、逐位排序
从个位开始,依次处理十位、百位...
每次处理完一位后,数组部分有序
最高位处理完成后数组完全有序
bash
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 获取数字指定位的数字(0-9)
int getDigit(int num, int exp) {
return (num / exp) % 10;
}
// 计数排序(基数排序子程序)
void countSort(int arr[], int n, int exp) {
int output[n]; // 输出数组
int count[10] = {0}; // 计数数组(0-9)
// 统计当前位数字出现次数
for (int i = 0; i < n; i++)
count[getDigit(arr[i], exp)]++;
// 计算累计分布(确定元素位置)
for (int i = 1; i < 10; i++)
count[i] += count[i - 1];
// 从后向前构建输出数组(保证稳定性)
for (int i = n - 1; i >= 0; i--) {
int digit = getDigit(arr[i], exp);
output[count[digit] - 1] = arr[i];
count[digit]--;
}
// 将排序结果复制回原数组
for (int i = 0; i < n; i++)
arr[i] = output[i];
}
// 基数排序主函数
void radixSort(int arr[], int n) {
// 寻找数组最大值确定最大位数
int max = arr[0];
for (int i = 1; i < n; i++)
if (arr[i] > max) max = arr[i];
// 对每一位进行计数排序
int exp = 1;
while (max / exp > 0) {
countSort(arr, n, exp);
exp *= 10;
}
}
// 测试代码
int main() {
int arr[] = {170, 45, 75, 90, 802, 24, 2, 66};
int n = sizeof(arr) / sizeof(arr[0]);
radixSort(arr, n);
printf("排序结果:");
for (int i = 0; i < n; i++)
printf("%d ", arr[i]);
return 0;
}
方法二(使用链表):
c
#include <stdio.h>
#include <stdlib.h>
// 链表节点定义
typedef struct Node {
int data;
struct Node* next;
} Node;
// 创建新节点
Node* createNode(int data) {
Node* newNode = (Node*)malloc(sizeof(Node));
if (!newNode) {
printf("内存分配失败\n");
exit(1);
}
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// 向链表尾部添加节点(使用头尾指针便于快速插入)
void append(Node** head, Node** tail, int data) {
Node* newNode = createNode(data);
if (*head == NULL) {
*head = newNode;
*tail = newNode;
} else {
(*tail)->next = newNode;
*tail = newNode;
}
}
// 打印链表
void printList(Node* head) {
Node* cur = head;
while (cur) {
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
// 获取链表中的最大值
int getMax(Node* head) {
if (!head) return 0;
int maxVal = head->data;
Node* cur = head->next;
while (cur) {
if (cur->data > maxVal)
maxVal = cur->data;
cur = cur->next;
}
return maxVal;
}
// 基数排序主函数(返回排序后的链表头)
Node* radixSort(Node* head) {
// 空链表或只有一个元素,无需排序
if (!head || !head->next) return head;
// 1. 获取最大值并计算位数
int maxVal = getMax(head);
if (maxVal == 0) return head; // 所有元素为0,直接返回
int maxDigits = 0;
int temp = maxVal;
while (temp) {
maxDigits++;
temp /= 10;
}
int divisor = 1; // 用于提取当前位的除数
for (int digitPos = 0; digitPos < maxDigits; digitPos++) {
// 2. 初始化10个桶(每个桶用头尾指针表示)
Node* buckets[10] = {NULL};
Node* bucketTails[10] = {NULL};
// 3. 分配阶段:遍历原链表,将节点按当前位放入对应桶
Node* cur = head;
while (cur) {
Node* nextNode = cur->next; // 保存下一个节点
cur->next = NULL; // 断开与原链表的连接
int digit = (cur->data / divisor) % 10; // 计算当前位数字
if (buckets[digit] == NULL) {
buckets[digit] = cur;
bucketTails[digit] = cur;
} else {
bucketTails[digit]->next = cur;
bucketTails[digit] = cur;
}
cur = nextNode; // 继续处理下一个节点
}
// 4. 收集阶段:按顺序(0~9)将桶中节点连接成新链表
Node* newHead = NULL;
Node* newTail = NULL;
for (int i = 0; i < 10; i++) {
if (buckets[i]) {
if (!newHead) {
newHead = buckets[i];
newTail = bucketTails[i];
} else {
newTail->next = buckets[i];
newTail = bucketTails[i];
}
}
}
head = newHead; // 更新链表头,准备下一轮排序
divisor *= 10; // 移向下一位
}
return head;
}
// 释放链表内存
void freeList(Node* head) {
Node* cur = head;
while (cur) {
Node* temp = cur;
cur = cur->next;
free(temp);
}
}
// 测试主函数
int main() {
Node* head = NULL;
Node* tail = NULL;
// 测试数据
int arr[] = {170, 45, 75, 90, 2, 24, 802, 66};
int n = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < n; i++)
append(&head, &tail, arr[i]);
printf("原始链表: ");
printList(head);
head = radixSort(head);
printf("排序后链表: ");
printList(head);
freeList(head);
return 0;
}
C语言算法实现(MSD版本)
代码中使用了递归和链表
c
#include <stdio.h>
#include <stdlib.h>
// 链表节点定义
typedef struct Node {
int data;
struct Node* next;
} Node;
// 创建新节点
Node* createNode(int data) {
Node* newNode = (Node*)malloc(sizeof(Node));
if (!newNode) {
printf("内存分配失败\n");
exit(1);
}
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// 向链表尾部添加节点(使用头尾指针便于快速插入)
void append(Node** head, Node** tail, int data) {
Node* newNode = createNode(data);
if (*head == NULL) {
*head = newNode;
*tail = newNode;
} else {
(*tail)->next = newNode;
*tail = newNode;
}
}
// 打印链表
void printList(Node* head) {
Node* cur = head;
while (cur) {
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
// 获取链表中的最大值
int getMax(Node* head) {
if (!head) return 0;
int maxVal = head->data;
Node* cur = head->next;
while (cur) {
if (cur->data > maxVal)
maxVal = cur->data;
cur = cur->next;
}
return maxVal;
}
// 获取最大值的最高位权重(例如802返回100,45返回10,2返回1)
int getMaxDivisor(int maxVal) {
int divisor = 1;
while (maxVal / 10 > 0) {
divisor *= 10;
maxVal /= 10;
}
return divisor;
}
// 递归MSD基数排序核心函数
Node* msdRadixSortRec(Node* head, int divisor) {
// 递归终止条件:空链表、单节点或已处理完所有位
if (head == NULL || head->next == NULL || divisor == 0) {
return head;
}
// 初始化10个桶(每个桶用头尾指针表示)
Node* buckets[10] = {NULL};
Node* bucketTails[10] = {NULL};
// 分配阶段:遍历链表,将节点按当前位放入对应桶
Node* cur = head;
while (cur) {
Node* nextNode = cur->next; // 保存下一个节点
cur->next = NULL; // 断开与原链表的连接
int digit = (cur->data / divisor) % 10; // 计算当前位数字
if (buckets[digit] == NULL) {
buckets[digit] = cur;
bucketTails[digit] = cur;
} else {
bucketTails[digit]->next = cur;
bucketTails[digit] = cur;
}
cur = nextNode; // 继续处理下一个节点
}
// 递归对每个非空桶进行下一级排序(移向下一位)
for (int i = 0; i < 10; i++) {
if (buckets[i] != NULL) {
buckets[i] = msdRadixSortRec(buckets[i], divisor / 10);
}
}
// 收集阶段:按顺序(0~9)将桶中节点连接成新链表
Node* newHead = NULL;
Node* newTail = NULL;
for (int i = 0; i < 10; i++) {
if (buckets[i] != NULL) {
if (newHead == NULL) {
newHead = buckets[i];
// 找到新链表的尾部
newTail = newHead;
while (newTail->next) newTail = newTail->next;
} else {
newTail->next = buckets[i];
// 更新尾部到当前桶的末尾
while (newTail->next) newTail = newTail->next;
}
}
}
return newHead;
}
// MSD基数排序主函数(封装递归调用)
Node* msdRadixSort(Node* head) {
if (!head || !head->next) return head; // 空或单元素直接返回
int maxVal = getMax(head);
if (maxVal == 0) return head; // 所有元素为0,直接返回
int divisor = getMaxDivisor(maxVal); // 最高位权重
return msdRadixSortRec(head, divisor);
}
// 释放链表内存
void freeList(Node* head) {
Node* cur = head;
while (cur) {
Node* temp = cur;
cur = cur->next;
free(temp);
}
}
// 测试主函数
int main() {
Node* head = NULL;
Node* tail = NULL;
// 测试数据
int arr[] = {170, 45, 75, 90, 2, 24, 802, 66};
int n = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < n; i++)
append(&head, &tail, arr[i]);
printf("原始链表: ");
printList(head);
head = msdRadixSort(head);
printf("排序后链表: ");
printList(head);
freeList(head);
return 0;
}
优化方向
1、基数选择:可采用更大基数(如256)减少位数d
2、负数处理:通过偏移量处理负数(如先分离正负数排序后合并)
3、动态内存:使用动态数组减少空间浪费
4、并行计算:不同位数的排序可并行化处理