桶排序 / 计数排序

在这里既然考虑的只是英文字母,所以各元素的取值无非26种可能,因此我们只需将表长M取作为26,并相应的建立这样一个散列表。而其中的各个桶单元呢,则依次对应于abc一直到z这26个字母。

在建立了这样一个散列表之后,我们的第一项任务就是来填充名为count的这一行。

顾名思义 其中的每一个元素都是一个计数器,分别记录其所对应的那个字母,在输入序列中所出现的次数。

比如,这个2就意味着对应的字母G,在输入序列中总共出现了2次。相应的,B只出现了1次,而A H K之类的字母根本就没有出现。当然 这里的5也意味着M在输入序列中总共出现了5次。

我们不妨来确认一下,1 2 3 4和5 不多不少。

那么,由输入序列如何快速的得到这样一张统计表呢?我说如果输入的规模为n,那么我们应该可以在O(n)的线性时间内完成这个任务,你能想出具体的方法吗?没错,只需遍历一次整个输入集。在此过程中,每遇到一个字符,就对它所对应的那个计数器做累加。形象的说 这样一个过程就犹如将所有的元素分别扔到它所对应的桶单元中,因此这一步骤也称作分配 distribution。

在这幅图中 请留意蓝色的折线,它是什么呢,没错 这条折线恰恰就对应于刚才我们的统计结果。

然而这还不是我们最终所需要的,我们还需要什么呢,这条红色的折线。

此前的那条蓝色折线被称作count是因为它反映了各字符在输入序列中出现的次序,而新的这条红线呢则称作accumulation。

这暗示着它是某种累计值,没错 ,累计值。更精确的说,这条红色折线上的任何一个点,其实都对应于蓝色折线至此之前的所有数值的积分。既然蓝色折线是非负的,所以作为它的积分,这条红色折线是单调非降的。那么红色折线上的这样每一个积分值具体又是什么含义呢

以这里的字母F为例,它总共出现了一次,而对应的积分值为6,这就说明在输入序列中,小于G的字母应该恰为6个。因此,作为这些字母中的最大者F,也自然应该被放在编号为5的位置。而同样的道理,因为字母G所对应的积分值为8,所以输入序列中的这两个字母G,在最终的有序序列中,自然地 也就应该被归入至6到8之间的这个区间 [ 6,8 )了,具体地 也就是6和7这两个单元.。

由此可见,只要我们能够得到每个字母的统计值以及累计值,就可以根据相邻字母的累计值,确定其在输出序列中所应处的区间范围。

参考资料:邓俊辉《数据结构/c++版》

发布了119 篇原创文章 · 获赞 152 · 访问量 25万+

猜你喜欢

转载自blog.csdn.net/weixin_40539125/article/details/99697422