一、原理
该算法借助一组桶单元实现对一组关键码的分拣,故称作桶排序(bucketsort)。其主要依赖于散列技术,首先借助散列函数将关键码映射到物理地址线性排列的桶单元中,这一个过程实际上完成了排序,然后遍历散列表中的非空桶将关键码读出即可。(散列表知识点:https://blog.csdn.net/qq_18108083/article/details/85134509)
二、应用场合
桶排序是针对具有大数据量和小范围特点的排序问题,如将人口普查中将中国十多亿人口按照其生日进行排序,数据量极大,但是范围却很小(0~365)。
(a) 简单例子
考查如下问题:给定范围为 [0, M) 内的n个互异整数 (n <=M) ,如何高效地对其排序?比如 M=10 和 n =5 的一个实例。
思考:自然,,向量排序器或列表排序器中的任一排序算法均可完成这一任务。但CBA式排序算法注定在最坏情况下需要Q(nlogn)时间,实际上,针对数值类型和取值范围特定的这一具体问题,完全可在更短的时间内完成排序。为此,引入长度为M的散列表。
接下来,使用最简单的散列函数 hash (key) = key,将这些整数视作关键码并逐一插入散列表中。最后,顺序遍历一趟该散列表,依次输出非空桶中存放的关键码,即可得到原整数集合的排序结果。
性能:该算法所用散列表共占O(M)空间。散列表的创建和初始化耗时O(M),将所有关键码插入散列表耗时O(n),依次读出非空桶中的关键O(M),故总体运行时间为O(n+M),在n >> M 的场合,桶排序算法的运行时间将是:O(n + M) = O(max(n,M)) = O(n)。
(b) 一般情况
若将上述问题进一步推广:若允许输入整数重复,又该如何高效地实现排序?
依然可以沿用以上构思,只不过这次需要处理散列冲突,具体地如上图所示,不妨采月独立链法排解冲突,在将所有整数作为关键码插入散列表之后,只需一趟顺序遍历将各非空桶的独立链依次串接起来,即可得到完整的排序结果。而且只要在串联时留意链表方向,则桶排序可以是稳定的。
性能:推广后的算法所用散列表共占O(M)空间,总体运行时间同样为O(n+M),在n >> M 的场合,桶排序算法的运行时间将是:O(n + M) = O(max(n,M)) = O(n)。
三、总结分析
在n >> M 的场合,桶排序算法的运行时间将是:O(n + M) = O(max(n,M)) = O(n)。线性正比于待排序元素的数目,突破了Ω(nlogn) 的下界。这是因为以上基于散列表的桶排序算法,采用的是循秩访问的方式,摒弃了以往基于关键码大小比较式的设计思路,故不在受CBA式算法固有的下界约束。正因为此,桶排序在算法设计方面也占有独特的地位。