基础闯关记-基数排序

基数排序

如果要问排序哪家快,那一定是基数排序,在线性时间内即可完成排序。在了解基数排序之前,我们先了解一下基础排序的低配版-桶排序。桶排序是一种典型的空间换时间的排序方法,小规模排序(比如1-100分数排序)是可以靠桶排序完成的,所以我们先介绍桶排序,然后看看桶排序的缺点,最后介绍基数排序

桶排序

桶排序很简单,首先按照待排序数组中最大的数申请多少个桶。比如排序5 1 2 5 3需要申请5个桶,然后从第一个元素开始,依次将元素丢入相应的桶中,最后只需要循环检查一点桶中的元素依次输出就实现排序了,下图表示桶排序过程

image

按照上图的流程,桶排序代码实现

#include<stdio.h>
void bucketSort(int arr[], int length);

void main(){
    int arr[] = {5, 1, 2, 5, 3};
    bucketSort(arr, 5);
    int k;
    for(k=0; k<5; k++){
        printf("%d ", arr[k]);
    }
}

void bucketSort(int arr[], int length){
    //申请6个桶
    int bucket[5];
    int i;
    for(i=0; i<6; i++){
        bucket[i] = 0;//初始化桶中元素数量为0
    }

    //将元素放入桶中
    int j;
    for(j=0; j<length; j++){
        bucket[arr[j]]++;
    }

    //处理结构
    int k;//桶的编号
    int g=0;//存放排序后数组编号
    for(k=0; k<6; k++){
        while(bucket[k] > 0){
            arr[g] = k;
            g++;
            bucket[k] --;
        }
    }
}

桶排序是能在线性时间内完成的,比插入排序,快速排序都要快。桶排序适合一定规模的数组排序(比如分数,年龄等)。但是一般不是这种情况,比如要排序2 1 999999。这个时候如果按照桶排序的思路,需要申请999999个桶,显然,空间浪费太大。所以接下来介绍基数排序来解决这个问题

基数排序

基数排序按照十进制的基数只有10个桶,从0-9编号。核心思想是先比较个位数,再比较十位数,再比较百位数...。所以每次比较其中一个数字,10个桶足够。比如要排序

64,8,216,512,27,729,0,1,343,125

下面就按照基数排序次位优先(先排低位)来进行

第一步:按照个位数分别入桶,比如64落在4号桶,8落在8号桶,216落在6号桶

image

第二步:在第一步的基础上,按照十位数依次入桶,0还是0号桶,1也是0号桶,512是1号桶。这个时候可能一个桶上有多个数字出现,可以用链表连接起来。

image

第三步:在第二部的基础上,按照百位数依次入桶,比如,0,1,8还是在0号桶,512在5号桶,216在2号桶。

image

最后,从第0个桶到第9个桶一次按照顺序输出这些元素,就是有序的:

0,1,8,27,64,125,216,512,719

基数排序的实现

由于每个桶内可能有多个元素,我们采用链表来存储元素信息。每个元素都是一个节点,这个节点的结构体如下

struct Node {
    int key;
    struct Node *next;
};

比如我们排序1 33 2 13 55,分三步完成我们的最低位排序过程

第一步,将数组放入链表中

image

第二步,将第一步链表中的元素按照最低位放入桶中,注意,桶也是一个链表,刚开始初始化的时候是空链表(head=tail=null),放入桶中之后,元素被分成了三部分,橙色链表,红色链表,蓝色链表。

image

第三步,按照桶的顺序(0-9)将链表再次连接起来,这里即把橙色,红色,蓝色链表连接起来。

image

然后生成新的链表之后,又可以回到第一步,这不过这一回是按照十位数进行排序了。我们用C语言代码实现上述步骤。

#include<stdio.h>
#include <stdlib.h>

//定义最大位数为4 基数是十进制
#define MaxDigit 4
#define Radix 10

//定义元素节点
struct Node {
    int key;
    struct Node * next;
};
typedef struct Node *PtrToNode;

// 桶节点,头和尾组成, 每个桶放一个链表
struct BucketNode {
    PtrToNode head;
    PtrToNode tail;
};

void bucketSort(int arr[], int length);
int getDigit(int, int);

int main(){
    int arr[] = {1, 15, 3, 25, 55, 7};
    bucketSort(arr, 6);
    int i;
    for(i=0; i<6; i++){
        printf("%d ", arr[i]);
    }
}

//获取相应位数的基数
int getDigit ( int x, int d )
{
    int di, i;
     
    for (i = 1; i<=d; i++) {
        di = x % Radix;
        x /= Radix;
    }
    return di;
}

//基数排序实现
void bucketSort(int arr[], int length){
    //第一步,把数组串成链表
    PtrToNode List = NULL;
    PtrToNode tmp;
    int i;
    for(i=0; i<length; i++){
        tmp = (PtrToNode)malloc(sizeof(struct Node));
        tmp->key = arr[i];
        tmp->next = List;
        List = tmp;
    }

    //初始化桶,0-9号桶,每个都先初始化空链表
    struct BucketNode BList[Radix];
    int j;
    for(j=0; j<Radix; j++){
        BList[j].head = NULL;
        BList[j].tail = NULL;
    }

    int k;
    for(k = 1; k < MaxDigit; k++){
        PtrToNode p;
        p = List;
        //第二步,遍历数组串成的队列,按照最末尾数组入桶中的队列
        while(p){
            //取出一个元素放在tmp中
            tmp = p;
            p = p->next;
            tmp->next = NULL;

            int di = getDigit(tmp->key, k);
            if(!BList[di].head){
                BList[di].head = BList[di].tail = tmp;
            }else{
                BList[di].tail->next = tmp;
                BList[di].tail = tmp;
            }
        }

        //第三步,检查每个桶中链表是否为空,将不为空的链表再次首尾连接起来
        List = NULL;
        int t;
        for(t=Radix -1; t >= 0; t--){
            if(BList[t].head){
                BList[t].tail->next = List;
                List = BList[t].head;
                BList[t].head = BList[t].tail = NULL; /* 清空桶 */
            }
        }
    }

     /* 将List倒入arr[]并释放空间 */
     for (i=0; i<length; i++) {
        tmp = List;
        List = List->next;
        arr[i] = tmp->key;
        free(tmp);
     } 
}

转载于:https://www.cnblogs.com/jianbingguozi/p/7078605.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值