数据结构与算法基础-学习-34-基数排序(桶排序)

目录

一、基本思想

二、算法思路

1、个位排序

(1)分配

(2)收集

2、十分位排序

(1)分配

(2)收集

三、源码分享

1、InitMyBucket

2、DestroyMyBucket

3、ClearMyBucket

4、PushData2Bucket

5、PopDataFromBucket

6、GetIntegerDigit

7、BucketSortSentryQueue

四、算法效率

五、Linux环境编译测试


排序的其他相关知识点和源码分享可以参考之前的博客:   

数据结构与算法基础-学习-30-插入排序之直接插入排序、二分插入排序、希尔排序》,

数据结构与算法基础-学习-31-交换排序之冒泡排序、快速排序》,

数据结构与算法基础-学习-32-选择排序之简单选择排序、堆排序》,

数据结构与算法基础-学习-33-归并排序

一、基本思想

基数排序的基本思想就是分配和收集。

基数排序也叫桶排序、箱排序,设置若干个桶,将关键字为k的记录放入第k个桶,然后再按照序号将非空的连接。

二、算法思路

我们还是以升序为例,初始化10个桶来存放数据,因为上面的数据最多到十分位,我们只需要两部就可以完成排序。

1、个位排序

(1)分配

10的个位是0,放到0号桶。

34的个位是4,放到4号桶。

1的个位是1,放到1号桶。

后面的数据以此类推。

(2)收集

我们按照顺序从第0个桶、第1个桶。。。。的顺序取数据,可以发现个位已经有序。并且只有个位的元素就不需要进行下一轮十分位的排序,我们只用比较有十分位的元素,这样可以减少排序时间。收集前桶中数据是全部,为了效率我们可以直接把只有个位的放入原序列中。

2、十分位排序

(1)分配

我们清空桶,将临时队列中的元素按照十分位的数值放入桶中。

(2)收集

我们按照顺序从第0个桶、第1个桶。。。。的顺序取数据,由于这些元素只有最大十分位,我们可以直接放入原队列中,这样就排好序啦。

三、源码分享

1、InitMyBucket

Status InitMyBucket(MyBucket** Bucket, QueueLenType BucketGroupNums, QueueLenType OneBucketNums, JudgeTypeFlag Flag)
{
    JudgeAllNullPointer(Bucket);

    if (BucketGroupNums * OneBucketNums > __LONG_LONG_MAX__)
    {
        LogFormat(Error,"Init Bucket Fail, Reason : BucketGroupNums(%lld) * OneBucketNums(%lld) > %lld.\n",
                  BucketGroupNums,OneBucketNums,__LONG_LONG_MAX__);
        return FailFlag;
    }

    QueueLenType i;

    (*Bucket)                       = (MyBucket*)MyMalloc(sizeof(MyBucket));
    (*Bucket)->BucketArrayMaxLen    = BucketGroupNums;
    (*Bucket)->BucketDataUseNums    = 0;
    (*Bucket)->BucketDataMaxUseNums = BucketGroupNums * OneBucketNums;
    (*Bucket)->BucketArray          = (SqQueue**)MyMalloc(BucketGroupNums * sizeof(SqQueue*));

    for ( i = 0; i < (*Bucket)->BucketArrayMaxLen; i++)
    {
        InitSqQueue(&((*Bucket)->BucketArray[i]),OneBucketNums,Flag);
    }
    
    LogFormat(Debug,"Init Bucket OK.\n");

    return SuccessFlag;
}

2、DestroyMyBucket

Status DestroyMyBucket(MyBucket** Bucket)
{
    JudgeAllNullPointer(*Bucket);

    QueueLenType i;

    for ( i = 0; i < (*Bucket)->BucketArrayMaxLen; i++)
    {
        DestroySqQueue(&((*Bucket)->BucketArray[i]));
    }

    free((*Bucket)->BucketArray);
    (*Bucket)->BucketArray          = NULL;
    (*Bucket)->BucketArrayMaxLen    = 0;
    (*Bucket)->BucketDataUseNums    = 0;
    (*Bucket)->BucketDataMaxUseNums = 0;
    free(*Bucket);
    *Bucket                         = NULL;
    
    LogFormat(Debug,"Destroy Bucket OK.\n");

    return SuccessFlag;
}

3、ClearMyBucket

Status ClearMyBucket(MyBucket* Bucket)
{
    JudgeAllNullPointer(Bucket);

    QueueLenType i;

    for ( i = 0; i < Bucket->BucketArrayMaxLen; i++)
    {
        ClearSqQueue(Bucket->BucketArray[i]);
    }
    Bucket->BucketDataUseNums = 0;

    LogFormat(Debug,"Clear Bucket OK.\n");

    return SuccessFlag;
}

4、PushData2Bucket

//将数据压入桶中。
Status PushData2Bucket(MyBucket* Bucket, QueueLenType BucketGroupIndex, void* Data)
{
    JudgeAllNullPointer(Bucket);
    JudgeAllNullPointer(Data);

    if (BucketGroupIndex < 0 || BucketGroupIndex >= Bucket->BucketArrayMaxLen)
    {
        LogFormat(Error,"Push Data To Bucket Fail, Reason : Illegal BucketGroupIndex(%lld).\n",BucketGroupIndex);
        return FailFlag;
    }

    if (Bucket->BucketDataUseNums == Bucket->BucketDataMaxUseNums)
    {
        LogFormat(Warning,"Push Data To Bucket Fail, Reason : Bucket Is Full(%lld).\n",Bucket->BucketDataMaxUseNums);
        return NormalFlag;
    }

    Status ReturnStatus;

    ReturnStatus = EnterSqQueue(Bucket->BucketArray[BucketGroupIndex],Data);
    if (ReturnStatus == SuccessFlag)
    {
        Bucket->BucketDataUseNums++;
        LogFormat(Debug,"Push Data To Bucket OK.\n");
    }
    
    return ReturnStatus;
}

5、PopDataFromBucket

//将数据从桶中取出来。
Status PopDataFromBucket(MyBucket* Bucket, QueueLenType BucketGroupIndex, void* Data)
{
    JudgeAllNullPointer(Bucket);
    JudgeAllNullPointer(Data);

    if (BucketGroupIndex < 0 || BucketGroupIndex >= Bucket->BucketArrayMaxLen)
    {
        LogFormat(Error,"Pop Data From Bucket Fail, Reason : Illegal BucketGroupIndex(%lld).\n",BucketGroupIndex);
        return FailFlag;
    }
    
    if (Bucket->BucketDataUseNums == 0)
    {
        LogFormat(Warning,"Pop Data From Bucket Fail, Reason : Bucket Is Empty.\n");
        return NormalFlag;
    }

    Status ReturnStatus;

    ReturnStatus = LeaveSqQueue(Bucket->BucketArray[BucketGroupIndex],Data);
    if (ReturnStatus == SuccessFlag)
    {
        Bucket->BucketDataUseNums--;
        LogFormat(Debug,"Pop Data From Bucket OK.\n");
    }

    return ReturnStatus;
}

6、GetIntegerDigit

//给出一个正整数,和你想要的位数,返回相应的位数。
//例如1234,你要十分位,返回一个3。
//1表示个位,2表示十分位,以此类推。 
//目前只支持int类型
int GetIntegerDigit(int Num, int Digit)
{
    // LogFormat(Debug,"Num : %d, Digit : %d\n",Num,Digit);
    if (Digit < 1 || Digit > 9)
    {
        return GET_INTEGER_DIGIT_FAIL_FLAG;
    }

    if (Num < 0)
    {
        return GET_INTEGER_DIGIT_FAIL_FLAG;
    }
    
    if (Digit == 1)
    {
        return Num % 10;
    }
    else if (MyIntSquare(10,Digit - 1) > Num)//如果Num不存在Digit相应的位数,如89不存在百分位的情况。
    {
        return GET_INTEGER_DIGIT_NO_EXISTS_FLAG;
    }
    else
    {
        return (Num % MyIntSquare(10,Digit) - Num % MyIntSquare(10,Digit - 1)) / MyIntSquare(10,Digit - 1);
    }
}

7、BucketSortSentryQueue

//由于GetIntegerDigit实现的原因,导致BucketSortSentryQueue只支持正整数排序。
//此函数如果执行出错,会改变Queue的值,里面存了中间结果。
Status BucketSortSentryQueue(SqQueue* Queue)
{
    JudgeAllNullPointer(Queue);

    MyBucket* Bucket   = NULL;
    SqQueue*  TmpQueue = NULL;//临时队列,存放中间数据。

    switch(Queue->Flag)
    {
        case INT_TYPE_FLAG  :
            InitMyBucket(&Bucket,INTEGER_BUCKET_NUMS,Queue->SqQueueLen,Queue->Flag);
            InitSqQueue(&TmpQueue,Queue->SqQueueLen,Queue->Flag);
            break;
        default :
            LogFormat(Error,"BucketSortSentry Function , Queue->Flag(%d) Is Unknow Type Flag, Exit!!!\n",Queue->Flag);
            exit(ExceptionExitFlag);
    }

    //后续再做成万能数据型
    //现在只支持整型
    int          ReutrnVal        = 0;
    QueueLenType i;
    QueueLenType BucketGroupIndex = 0;
    Status       ReturnStatus;
    int          Digit            = 1;//计算的位数
    QueueLenType MaxQueueLen      = Queue->SqQueueLen;

    do
    {
        if (Digit != 1)
        {
            //第n次收集是从TmpQueue读取数据,做整数Digit位的排序,放入桶中。
            for ( i = 0; i < TmpQueue->SqQueueLen; i++)
            {
                ReadSqQueue(TmpQueue,i,&ReutrnVal);
                BucketGroupIndex = GetIntegerDigit(ReutrnVal,Digit);
                PushData2Bucket(Bucket, BucketGroupIndex, &ReutrnVal);
            }
            //清理临时队列。
            ClearSqQueue(TmpQueue);
        }
        else
        {
            //第一次收集是从传入参数Queue读取数据,做整数个位的排序,放入桶中。
            for ( i = 1; i < Queue->SqQueueLen; i++)
            {
                ReadSqQueue(Queue,i,&ReutrnVal);
                BucketGroupIndex = GetIntegerDigit(ReutrnVal,Digit);
                PushData2Bucket(Bucket, BucketGroupIndex, &ReutrnVal);
            }

            ReadSqQueue(Queue,0,&ReutrnVal);
            ClearSqQueue(Queue);
            EnterSqQueue(Queue,&ReutrnVal);
        }

        //第n次分配,从桶中把顺序数据读取出来。
        i = 0;
        Digit++;
        while (Bucket->BucketDataUseNums != 0)
        {
            ReturnStatus = PopDataFromBucket(Bucket, i, &ReutrnVal);
            if (ReturnStatus == SuccessFlag)//成功读出数据,放入临时队列中。
            {
                if (GetIntegerDigit(ReutrnVal,Digit) == GET_INTEGER_DIGIT_NO_EXISTS_FLAG)//如果给的数没有Digit,放到最终队列中。
                {
                    EnterSqQueue(Queue,&ReutrnVal);
                }
                else if (GetIntegerDigit(ReutrnVal,Digit) != GET_INTEGER_DIGIT_FAIL_FLAG)//有Digit的进行下一步计算。
                {
                    EnterSqQueue(TmpQueue,&ReutrnVal);
                }
                else//异常情况
                {
                    LogFormat(Error,"Bucket Sort Sentry Queue Fail, Reason : Error Data(%d).\n",ReutrnVal);
                    exit(ExceptionExitFlag);
                }
            }
            else if (ReturnStatus == NormalFlag)//由于第i个桶的数据被读取完了,读下一个桶。
            {
                i++;
            }
            else//读取数据失败
            {
                DestroyMyBucket(&Bucket);
                DestroySqQueue(&TmpQueue);
                Bucket   = NULL;
                TmpQueue = NULL;
                LogFormat(Error,"Bucket Sort Sentry Queue Fail, Reason : Pop Data From Bucket Fail.\n");
                return FailFlag;
            }
        }
        //清理桶
        ClearMyBucket(Bucket);
    }while (GetSqQueueLen(Queue) < MaxQueueLen);//如果最终结果队列的元素个数小于Queue队列的元素个数,说明数据没有排序完。

    DestroyMyBucket(&Bucket);
    DestroySqQueue(&TmpQueue);
    Bucket   = NULL;
    TmpQueue = NULL;

    LogFormat(Debug,"Bucket Sort Sentry Queue OK.\n");

    return SuccessFlag;
}

四、算法效率

情况时间复杂度是否稳定
最好O(n + m)稳定
最坏O(k * (n + m))
平均O(k * (n + m))

例如我们上面这个计算时间复杂度是多少呢?

 (10个数字 + 10个桶)* 2位数 = 40

五、Linux环境编译测试

[gbase@czg2 Sort]$ make
gcc -Wall -Wextra -O3 InsertSort.c SwapSort.c SelectSort.c MergeSort.c BucketSort.c main.c -o TestSort -I /opt/Developer/ComputerLanguageStudy/C/DataStructureTestSrc/Log/ -I /opt/Developer/ComputerLanguageStudy/C/DataStructureTestSrc/PublicFunction/ -I /opt/Developer/ComputerLanguageStudy/C/DataStructureTestSrc/PublicFunction/HashTable/include/ -I /opt/Developer/ComputerLanguageStudy/C/DataStructureTestSrc/PublicFunction/SqQueue/ -I /opt/Developer/ComputerLanguageStudy/C/DataStructureTestSrc/PublicFunction/SqStack/ -L /opt/Developer/ComputerLanguageStudy/C/DataStructureTestSrc/PublicFunction/Make/Libs/ -lPublicFunction -lLog -lSqQueue
[gbase@czg2 Sort]$ time ./TestSort 
2023-9-12--[ Debug ]--Init SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Info  ]--SqQueue Data   :
Data           : [ 0 ,5 ,6 ,7 ,8 ,9 ,0 ,1 ,2 ,3 ,4 ]
FrontIndex     : 0
RearIndex      : 0
SqQueueLen     : 11
SqQueueMaxLen  : 11
Flag           : INT_TYPE_FLAG
2023-9-12--[ Debug ]--Init SqQueue OK
2023-9-12--[ Debug ]--Init SqQueue OK
2023-9-12--[ Debug ]--Init SqQueue OK
2023-9-12--[ Debug ]--Init SqQueue OK
2023-9-12--[ Debug ]--Init SqQueue OK
2023-9-12--[ Debug ]--Init SqQueue OK
2023-9-12--[ Debug ]--Init SqQueue OK
2023-9-12--[ Debug ]--Init SqQueue OK
2023-9-12--[ Debug ]--Init SqQueue OK
2023-9-12--[ Debug ]--Init SqQueue OK
2023-9-12--[ Debug ]--Init Bucket OK.
2023-9-12--[ Debug ]--Init SqQueue OK
2023-9-12--[ Debug ]--Read  SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Push Data To Bucket OK.
2023-9-12--[ Debug ]--Read  SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Push Data To Bucket OK.
2023-9-12--[ Debug ]--Read  SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Push Data To Bucket OK.
2023-9-12--[ Debug ]--Read  SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Push Data To Bucket OK.
2023-9-12--[ Debug ]--Read  SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Push Data To Bucket OK.
2023-9-12--[ Debug ]--Read  SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Push Data To Bucket OK.
2023-9-12--[ Debug ]--Read  SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Push Data To Bucket OK.
2023-9-12--[ Debug ]--Read  SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Push Data To Bucket OK.
2023-9-12--[ Debug ]--Read  SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Push Data To Bucket OK.
2023-9-12--[ Debug ]--Read  SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Push Data To Bucket OK.
2023-9-12--[ Debug ]--Read  SqQueue OK
2023-9-12--[ Debug ]--Clear SqQueue OK
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Leave SqQueue OK
2023-9-12--[ Debug ]--Pop Data From Bucket OK.
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--SqQueue is Empty, Data cannot be left
2023-9-12--[ Debug ]--Leave SqQueue OK
2023-9-12--[ Debug ]--Pop Data From Bucket OK.
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--SqQueue is Empty, Data cannot be left
2023-9-12--[ Debug ]--Leave SqQueue OK
2023-9-12--[ Debug ]--Pop Data From Bucket OK.
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--SqQueue is Empty, Data cannot be left
2023-9-12--[ Debug ]--Leave SqQueue OK
2023-9-12--[ Debug ]--Pop Data From Bucket OK.
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--SqQueue is Empty, Data cannot be left
2023-9-12--[ Debug ]--Leave SqQueue OK
2023-9-12--[ Debug ]--Pop Data From Bucket OK.
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--SqQueue is Empty, Data cannot be left
2023-9-12--[ Debug ]--Leave SqQueue OK
2023-9-12--[ Debug ]--Pop Data From Bucket OK.
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--SqQueue is Empty, Data cannot be left
2023-9-12--[ Debug ]--Leave SqQueue OK
2023-9-12--[ Debug ]--Pop Data From Bucket OK.
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--SqQueue is Empty, Data cannot be left
2023-9-12--[ Debug ]--Leave SqQueue OK
2023-9-12--[ Debug ]--Pop Data From Bucket OK.
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--SqQueue is Empty, Data cannot be left
2023-9-12--[ Debug ]--Leave SqQueue OK
2023-9-12--[ Debug ]--Pop Data From Bucket OK.
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--SqQueue is Empty, Data cannot be left
2023-9-12--[ Debug ]--Leave SqQueue OK
2023-9-12--[ Debug ]--Pop Data From Bucket OK.
2023-9-12--[ Debug ]--Enter SqQueue OK
2023-9-12--[ Debug ]--Clear SqQueue OK
2023-9-12--[ Debug ]--Clear SqQueue OK
2023-9-12--[ Debug ]--Clear SqQueue OK
2023-9-12--[ Debug ]--Clear SqQueue OK
2023-9-12--[ Debug ]--Clear SqQueue OK
2023-9-12--[ Debug ]--Clear SqQueue OK
2023-9-12--[ Debug ]--Clear SqQueue OK
2023-9-12--[ Debug ]--Clear SqQueue OK
2023-9-12--[ Debug ]--Clear SqQueue OK
2023-9-12--[ Debug ]--Clear SqQueue OK
2023-9-12--[ Debug ]--Clear Bucket OK.
2023-9-12--[ Debug ]--Destroy SqQueue OK
2023-9-12--[ Debug ]--Destroy SqQueue OK
2023-9-12--[ Debug ]--Destroy SqQueue OK
2023-9-12--[ Debug ]--Destroy SqQueue OK
2023-9-12--[ Debug ]--Destroy SqQueue OK
2023-9-12--[ Debug ]--Destroy SqQueue OK
2023-9-12--[ Debug ]--Destroy SqQueue OK
2023-9-12--[ Debug ]--Destroy SqQueue OK
2023-9-12--[ Debug ]--Destroy SqQueue OK
2023-9-12--[ Debug ]--Destroy SqQueue OK
2023-9-12--[ Debug ]--Destroy Bucket OK.
2023-9-12--[ Debug ]--Destroy SqQueue OK
2023-9-12--[ Debug ]--Bucket Sort Sentry Queue OK.
2023-9-12--[ Info  ]--Sort Function Elapsed Time   : 0 s
2023-9-12--[ Info  ]--SqQueue Data   :
Data           : [ 0 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ]
FrontIndex     : 0
RearIndex      : 0
SqQueueLen     : 11
SqQueueMaxLen  : 11
Flag           : INT_TYPE_FLAG
2023-9-12--[ Debug ]--Destroy SqQueue OK

real    0m0.002s
user    0m0.002s
sys     0m0.000s

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
十大经典排序算法是指在计算机科学中常用的排序算法,它们分别是: 1. 冒泡排序(Bubble Sort):重复地比较相邻的两个元素,将较大的元素逐渐向右移动。 2. 选择排序(Selection Sort):每次从未排序的部分选择最小(或最大)的元素,并放在已排序的部分的末尾。 3. 插入排序(Insertion Sort):将未排序的元素逐个插入到已排序的部分中的正确位置。 4. 希尔排序(Shell Sort):将待排序的数组按照一定步长进行分组,对每组进行插入排序,逐渐减小步长。 5. 归并排序(Merge Sort):将待排序的数组递归地分成两半,对每一半进行排序,然后合并两个有序数组。 6. 快速排序(Quick Sort):选择一个基准元素,将数组划分为两部分,左边部分都小于基准,右边部分都大于基准,递归地对两部分进行排序。 7. 堆排序(Heap Sort):将待排序的数组构建成一个最大堆(或最小堆),然后依次取出堆顶元素并调整堆结构。 8. 计数排序(Counting Sort):统计数组中每个元素出现的次数,然后根据统计结果对元素进行排序。 9. 桶排序(Bucket Sort):将待排序的数组划分为多个,对每个中的元素进行排序,最后将中的元素按顺序合并。 10. 基数排序(Radix Sort):按照元素的位数,将待排序的数组从低位到高位进行排序。 以上是十大经典排序算法,每种算法都有其适用的场景和性能特点,选择合适的排序算法可以提高程序的效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值