C语言---排序算法10---基数排序法

文章目录

原理

基数排序是一种非比较型整数排序算法,通过按位分配数字到桶中实现排序。其核心思想是"分配-收集"过程,分为两种策略:

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、并行计算:不同位数的排序可并行化处理

参考视频

数据结构合集 - 基数排序(算法过程, 效率分析, 稳定性分析)

相关推荐
YGGP2 小时前
【Golang】LeetCode 56. 合并区间
算法·leetcode·职场和发展
_F_y2 小时前
回文串系列动态规划附马拉车算法原理及实现
算法·动态规划
你怎么知道我是队长2 小时前
C语言---排序算法12---计数排序法
c语言·算法·排序算法
fu的博客2 小时前
【数据结构2】带头结点·单向链表实现
数据结构·算法·链表
近津薪荼2 小时前
优选算法——前缀和(6):和可被 K 整除的子数组
c++·算法
lifallen2 小时前
线性基 (Linear Basis)
数据结构·算法
twilight_4692 小时前
人工智能数学基础——第二章 高等数学基础
人工智能·算法·机器学习
_OP_CHEN2 小时前
【算法提高篇】(二)线段树之区间修改:懒标记的核心奥义与实战实现
算法·蓝桥杯·线段树·c/c++·区间查询·acm/icpc·懒标记
啊阿狸不会拉杆2 小时前
《机器学习导论》第 18 章-增强学习
人工智能·python·学习·算法·机器学习·智能体·增强学习