基数排序算法的Python实现(头歌教学实践平台)

第1关:基数排序的实现

任务描述

本关任务:编写代码实现基数排序。

相关知识

为了完成本关任务,你需要掌握: 1.如何实现基数排序; 2.基数排序的算法分析。

基数排序

大多数的排序算法都是通过比较数据大小的方式对列表进行排序,而基数排序与此不同,它不需要进行数据的比较与交换,而是通过“分配”和“收集”两个过程来实现排序。基数排序是桶排序的扩展,它的主要思想是多关键字排序,例如扑克牌有数字和花色两类关键字,可以先按数字将牌分配到 13 个桶中,然后从第一个桶开始依次收集。再将收集好的牌按花色分配到 4 个桶中,然后还是从第一个桶开始依次收集。经过两次“分配”和“收集”操作,最终使牌有序。

对于数字型的数据项,可以将该整数按位数切割成不同的数字,然后按每个位数分别排序。具体做法是:将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

下面的一个例子为对多位整数进行基数排序的过程,列表初始状态如图 1 所示。由于列表中的整数最多位数为 3 位,因此需要进行三趟排序。

 图1 初始列表

该列表中每个数据项的每一位都是由数字组成的,数字的范围是 0 到 9,所以准备 10 个桶用来放数据项,如图 2 所示。

 图2 初始桶

在算法实现中,这里的桶可以通过先进先出的队列来实现。以下为队列 Queue 类的定义,其中还定义了判断队列是否为空、入队、出队等方法。

class Queue:
    def __init__(self):
        self.items = []
    def isEmpty(self):     # 判空
        return self.items == []
    def enqueue(self, item):     # 入队
        self.items.insert(0,item)
    def dequeue(self):     # 出队
        return self.items.pop()
    def size(self):     # 求队列元素个数
        return len(self.items)
  1. 首先按照最后一位来进行第 1 趟分配和收集。

(1)分配过程(从桶上方进入):

  • 278 最低位是 8,放到桶 8 中;
  • 109 最低位是 9,放到桶 9 中;
  • 063 最低位是 3,放到桶 3 中;
  • 930 最低位是 0,放到桶 0 中;
  • 589 最低位是 9,放到桶 9 中;
  • 184 最低位是 4,放到桶 4 中;
  • 505 最低位是 5,放到桶 5 中;
  • 269 最低位是 9,放到桶 9 中;
  • 008 最低位是 8,放到桶 8 中;
  • 083 最低位是 3,放到桶 3 中。

第 1 趟分配过程完成,结果如图 3 所示。

 图3 第 1 趟分配的结果

(2)收集过程按桶 0 到桶 9 的顺序收集(数据项从桶下方出):

  • 桶 0:930
  • 桶 1:没数据项,不收集
  • 桶 2:没数据项,不收集
  • 桶 3:063,083
  • 桶 4:184
  • 桶 5:505
  • 桶 6:没数据项,不收集
  • 桶 7:没数据项,不收集
  • 桶 8:278,008
  • 桶 9:109,589,269

第 1 趟收集的结果如图 4 所示,可以看到,整个列表最低位有序了。

 图4 第 1 趟收集的结果

  1. 然后在第 1 趟排序结果的基础上,按中间位来进行第 2 趟分配和收集。

(1)分配过程(从桶上方进入):

  • 930 中间位是 3,放到桶 3 中;
  • 063 中间位是 6,放到桶 6 中;
  • 083 中间位是 8,放到桶 8 中;
  • 184 中间位是 8,放到桶 8 中;
  • 505 中间位是 0,放到桶 0 中;
  • 278 中间位是 7,放到桶 7 中;
  • 008 中间位是 0,放到桶 0 中;
  • 109 中间位是 0,放到桶 0 中;
  • 589 中间位是 8,放到桶 8 中;
  • 269 中间位是 6,放到桶 6 中。

第 2 趟分配过程完成,结果如图 5 所示。

 图5 第 2 趟分配的结果

(2)收集过程按桶 0 到桶 9 的顺序收集(数据项从桶下方出):

  • 桶 0:505,008,109
  • 桶 1:没数据项,不收集
  • 桶 2:没数据项,不收集
  • 桶 3:930
  • 桶 4:没数据项,不收集
  • 桶 5:没数据项,不收集
  • 桶 6:063,269
  • 桶 7:278
  • 桶 8:083,184,589
  • 桶 9:没数据项,不收集

第 2 趟收集的结果如图 6 所示,可以看到,此时中间位有序了,并且中间位相同的那些数据项,其最低位也是有序的。

 图6 第 2 趟收集的结果

  1. 最后在第 2 趟排序结果的基础上,按最高位来进行第 3 趟分配和收集。

(1)分配过程(从桶上方进入):

  • 505 最高位是 5,放到桶 5 中;
  • 008 最高位是 0,放到桶 0 中;
  • 109 最高位是 1,放到桶 1 中;
  • 930 最高位是 9,放到桶 9 中;
  • 063 最高位是 0,放到桶 0 中;
  • 269 最高位是 2,放到桶 2 中;
  • 278 最高位是 2,放到桶 2 中;
  • 083 最高位是 0,放到桶 0 中;
  • 184 最高位是 1,放到桶 1 中;
  • 589 最高位是 5,放到桶 5 中。

第 3 趟分配过程完成,结果如图 7 所示。

 图7 第 3 趟分配的结果

(2)收集过程按桶 0 到桶 9 的顺序收集(数据项从桶下方出):

  • 桶 0:008,063,083
  • 桶 1:109,184
  • 桶 2:269,278
  • 桶 3:没数据项,不收集
  • 桶 4:没数据项,不收集
  • 桶 5:505,589
  • 桶 6:没数据项,不收集
  • 桶 7:没数据项,不收集
  • 桶 8:没数据项,不收集
  • 桶 9:930

第 3 趟收集的结果如图 8 所示,可以看到,此时最高位有序了,并且最高位相同的数据项按中间位有序,中间位相同的数据项按最低位有序。于是整个列表有序,基数排序过程结束。

 图8 第 3 趟收集的结果

基数排序的算法分析

基数排序平均和最坏情况下的时间复杂度都是O(d(n+r)),其中:

  • d 为数据项的关键字位数,即分配和收集的趟数,如 930 是由 3 位数组成,所以 d 为 3;
  • n 为列表中数据项的个数,即每一趟分配需要放入桶中的元素个数;
  • r 为关键字的取值范围,即每一趟需要收集的桶数,如 930 的每一位都是数字,取值范围是 0 到 9,所以 r 为 10。

编程要求

在右侧编辑器中的 Begin-End 区间补充代码,根据基数排序的算法思想和队列的基本操作完成radix_sort方法,从而实现对无序表的排序。

测试说明

平台会对你编写的代码进行测试,比对你输出的数值与实际正确的数值,只有所有数据全部计算正确才能通过测试:

测试输入:

  1. 278,109,63,930,589,184,505,269,8,83

输入说明:输入为需要对其进行排序的无序表。

预期输出:

  1. [8, 63, 83, 109, 184, 269, 278, 505, 589, 930]

输出说明:输出的是对无序表进行基数排序后的结果,以列表的形式展现。

测试输入:

  1. 54,26,93,17,77,31,44,55,20

预期输出:

  1. [17, 20, 26, 31, 44, 54, 55, 77, 93]

开始你的任务吧,祝你成功!

'''请在Begin-End之间补充代码, 完成radix_sort函数'''

# 队列
class Queue:
    def __init__(self):
        self.items = []

    def isEmpty(self):    # 判空
        return self.items == []

    def enqueue(self, item):     # 入队
        self.items.insert(0,item)

    def dequeue(self):     # 出队
        return self.items.pop()

    def size(self):
        return len(self.items)
        
# 基数排序
def radix_sort(s):
    main = Queue()
    for n in s:
        main.enqueue(n)   # 所有数据项入队
    d = len(str(max(s)))  # 求出最大数据项的位数
    dstr = "%%0%dd" % d    # 前导零的模板,把数据项补齐为d位,位数不够的前面置0,如"%05d"表示补齐成五位数
    nums = [Queue() for _ in range(10)]   # 准备10个队列
    for i in range(-1, -d-1, -1):   # i的取值为-1到-d,代表从个位到最高位
        while not main.isEmpty():   # 进行分配
            n = main.dequeue()
            dn = (dstr % n)[i]  # 得到出队数据的第i位,转成类似"00345"[-2],即得到倒数第二位的4
            nums[int(dn)].enqueue(n)  # 放到对应的队列中
        # 从10个队列中收集到main
        # ********** Begin ********** #    
        for i in range(10):
            while not nums[i].isEmpty():
                main.enqueue(nums[i].dequeue())
        # ********** End ********** #
    # 从main导出为列表
    result = []
    while not main.isEmpty():
        result.append(main.dequeue())
    return result

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值