![981180e4f27ee552772f9913401deaad.png](https://i-blog.csdnimg.cn/blog_migrate/f6f02ad0db2494124818d95146c3c853.jpeg)
前言
接下俩要介绍的是整个排序部分最后一点内容,关于计数排序与基数排序,之前的几讲详细分析过基于比较的排序方法算法的下界
一、计数排序
这部分内容可以参考菜鸟教程,讲解的较为详细,并有示例代码。我将简单说明一下计数排序的主要过程和主要思想。
计数排序是一种时间复杂度为
- 找出待排序数组中最大和最小的元素。
显然,这里是1和5
2. 统计数组中每个值为i的元素的出现次数,这里借助数组C。
我们从左到右遍历待排序列,每个元素出现的次数存入C[i]位置。
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
1 | 2 | 1 | 1 |
最后得到如上的数组。
3. 对所有的计数累加(如从C中的第一个元素开始,每一项和前一项相加)
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
1 | 1 | 3 | 4 | 5 |
4. 反向填充目标数组:反向遍历原待排序列,这里也是为了保证计数排序是稳定的。
[3, 1, 4, 5, 3]
取最后一个元素3,到C[3]位置找到对应的计数3,将其放置到一个新数组F中,并将计数减1。
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
1 | 1 | 2 | 4 | 5 |
F
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
3 |
然后再取下一个元素5,还是按照上面的过程,最终我们得到数组F
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 |
计数排序的基本思想就是求解某个元素比它小的数据有几个,根据这个信息来决定当前元素排列位置。
二、基数排序基本过程
基数排序是另一种时间复杂度为
基数排序采用了一种桶的概念,根据关键字来划分桶。我们以扑克牌举列子,假设我们要对52张扑克牌进行排序,定义花色的大小为“♠<♣<♦<♥”,牌面数字顺序为“2<3<...<A”。显然♠2是最小的牌,♥A是最大的牌,很容易看到花色和数字是两类关键字,先对一种关键字排序,再对另一种关键字排序,就是基数排序的思想。
①选取数字划分13个桶,对所有的牌进行划分(不失一般性,会出现如下的情况)
♥ | ♠ | ...... | ♣ | ♦ |
---|---|---|---|---|
♦ | ♥ | ...... | ♠ | ♣ |
♣ | ♦ | ...... | ♥ | ♠ |
♠ | ♣ | ...... | ♦ | ♥ |
2 | 3 | ...... | K | A |
②将上面的13个桶收集成一个队列,前一个桶的队首接到后一个队列的队尾
♠2-->♣2-->...-->♣3-->♦3-->...-->♦A
③选取花色作为第二个关键字,分成4个桶,对②中的序列进行再次划分
A | A | A | A |
---|---|---|---|
K | K | K | K |
Q | Q | Q | Q |
J | J | J | J |
10 | 10 | 10 | 10 |
9 | 9 | 9 | 9 |
8 | 8 | 8 | 8 |
7 | 7 | 7 | 7 |
6 | 6 | 6 | 6 |
5 | 5 | 5 | 5 |
4 | 4 | 4 | 4 |
3 | 3 | 3 | 3 |
2 | 2 | 2 | 2 |
1 | 1 | 1 | 1 |
♠ | ♣ | ♦ | ♥ |
④ 再次按照步骤②的收集方式采集一次,就得到有序的扑克牌排序序列。
上面这种排序思路就是基数排序的排序思路。
和基数排序思想类似的还有一个就是桶排序,桶排序的基本操作:
- 分配关键字:根据每个数据元素关键字的值将其分配到对应的桶中
- 每个“桶”内排序
- 收集桶
桶排序的时间取决于每个桶内的排序,简单分析一下桶排序的时间复杂度
桶排序存在的一个问题是,分配和收集过程不是一趟统一完成的,而基数排序可以做到这一点,上面已经分析过基数排序的基本思想。
基数排序是一种借助多关键字排序来实现单关键字排序的内排序算法。
对于一个n个记录的序列
根据最主关键字和最次关键字的顺序,我们有两种基本的基数排序方法,一种是最主关键字优先的最高位优先MSD法,一种是最低位优先的LSD法。最高位法往往会来带诸多不便,因此我们一般采用低位优先。
最为经典和常见的就是三位数的排序,分别取个位十位和百位作为关键字,进行三趟排序完成基数排序过程。
![7c596099efe6d205f6557e72f200acc3.png](https://i-blog.csdnimg.cn/blog_migrate/c8f0a162f8f318051b87b8d15d383775.jpeg)
为了使分配和收集过程能够统一起来,我们使用链表作为存储结构,即链式基数排序。其具体的排序方法,这里可以参看本科数据结构课本知识,这里不做展开,下面我们进入时间复杂度分析。
三、基数排序时间复杂度分析
因为基数排序不采用比较的策略,因此之前基于比较次数衡量算法时间复杂度的方法在基数排序中并不适用,因此我们直接看基数排序过程。
① 需要确定基数排序的关键字个数d,挑选一个关键字做第一次分配。
② 根据挑选的关键字建立对应的桶数rd,时间复杂度
③ 分配过程需要操作所有元素,元素个数为n,时间复杂度
④ 对各个桶的结果进行收集,时间复杂度桶②为
将整个过程加和到一起,得到总时间复杂度为
四、代表性的习题
这里提出两道习题,供大家思考,以加深对基数排序的理解,如果不知道如何求解欢迎私聊。
- 1000个关键字小于10000的整数进行排序,给出一种排序方法,并分析你的方法有多快?
答案:会有一个约等于2000量级的排序算法。
2.已知一个含有k个记录的序列,其关键字均为介于0和
提示:使用K进制。
总结
到这里,排序算法基本已经介绍完毕,关于余下集中简单排序、选择排序和希尔排序不做时间复杂度分析,已经分析过的无论从经典性、使用频率和时间复杂度上都是最代表性的排序算法,希望经过之前几讲的讲解,可以加深你对排序算法时间复杂度的理解,更加清晰的了解各种排序算法。下一部分的内容将会介绍数据元素的选择。