(变长数据项的排序)
a. 给定一个整数数组,其中不同的整数所包含的数字的位数可能不同,但该数组中,所有整数中包含的总数字位数为。设计一个算法,使其可以在时间内对该数组进行排序。
b. 给定一个字符串数组,其中不同的字符串所包含的字符数可能不同,但所有字符串中的总字符个数为。设计一个算法,使其可以在时间内对该数组进行排序。(注意:此处的顺度是指标准的字典序,例如。)
解
a.
假设数组一共有个元素,各元素的位数分别为,显然有。如果采用基数排序,运行时间为,其中,这与题目要求的运行时间不符。例如,假设元素个数,有一个元素的位数为,其他个元素的位数都为,这种情况下基数排序的运行时间为。
为了可以在时间内完成排序,考虑先按照元素的位数对元素进行分组,相同位数的元素分为一组,然后采用基数排序分别对每组元素进行排序,然后按照位数从小到大依次输出每组元素即可完成排序。
该算法的时间复杂度取决于两部分,一部分是分组的时间,一部分是对各组元素进行排序的时间。分组的时间取决于确定各元素的位数所花费的时间,对于一个有位的元素,需要时间来确定它的位数。于是,确定所有元素的位数所花费的总时间为。对各组元素的排序采用基数排序。对于第组来说,假设其中有个元素,该组元素都包含个数位,基数排序的时间为。于是,对各组元素进行基数排序的总时间为。根据以上分析,该算法的时间复杂度为。
b.
同样考虑借用基数排序的思想。在对数字的基数排序中,排序从低位开始,再到高位。与此不同,在对字符串的基数排序中,排序先从最左边的字符开始,再到右边的字符。下面给出一个例子。
注意,第一轮排序对所有字符串的最左边的字符进行排序。在第一轮排序过后,所有字符串按照最左边的字符进行分组(如上图所示,第一轮排序过后,at和a为一组,box和bat为一组,fix、fox和fit为一组,I为一组)。因为要求按字典序来排序,所以可以断言,在最终排好序的序列中,第一组字符串肯定排在第二组字符串之前,第二组字符串也肯定排在第三组字符串之前……。于是在第二轮排序中,分别对第一轮分组后的各组元素进行组内排序,而不改变组与组之间的顺序。第二轮排序之后,又可以按各字符串的第二个字符进行分组,第三轮排序又针对第二轮的分组进行组内排序,如此递归下去,最后可以完成整个字符串序列的排序。
假如所有字符串都是C风格字符串(即字符串以’\0’结尾),假设第个字符串的长度为,那么在第轮排序中,该字符串以’\0’参与排序,它一定会排在所在分组的前列,因为字符’\0’的值小于其他任何字符的值,而所有长度大于的字符串都排在这个长度为字符串的后面,这符合字典序。所以在第轮排序中,这个长度为字符串字符串可以不用参与排序,只需要让所有长度大于的字符串参与排序。
根据以上分析,一个长度为的字符串会参与轮排序。如果每一轮排序都采用线性时间的计数排序,那么一个长度为的字符串在每一轮排序中贡献时间,在所有轮排序中贡献时间。于是,对所有字符串进行排序的总时间为
在上式中,为字符串的个数。假如没有空字符串,即所有字符串的长度至少为,则肯定有,所以在上式中是成立的。
下面组出该算法的伪代码。
要对整个字符串数组进行排序,只需要调用即可。
代码链接
https://github.com/yangtzhou2012/Introduction_to_Algorithms_3rd/tree/master/Chapter08/Problem_8-3