开篇以一个问题开始,有人问了作者一个问题:“怎样给一个磁盘文件排序”,我看到这个问题的第一反应是,要知道磁盘文件里面的数据结构是什么样子的,然后就根据文件里面的数据内容采用经典的排序算法,就可以解决该问题了。
继续往下看,作者的回答总结如下:“建议提问者深入研究算法教材,在程序设计书里有磁盘排序的程序,参考书里的实现,最多花费一周时间就可以实现并测试完成该代码”。
作者给了上述答复后,并没有让提问者满意,便开始了继续深入的交流。通过一系列的交流最后得出该问题的全景,全景如下:
待排序的文件中最多包含1千万条记录,每条记录都是7位的整数,每个记录只出现一次。由于各种限制,该程序只有1MB的内存可以使用。这个问题的背景是给美国的免费电话进行排序,问题的输入是电话号码的列表,期望的输出是以升序排列的电话号码列表,基于应用场景,对一次排序的性能时间要求为最多几分钟,10秒钟是最理想的运行时间。
基于以上的交流后,作者给出了准确的问题描述规范或者说是模板。针对以上全景,可将所有情况组织称以下形式:
输入:一个最多包含n个正整数的文件,每个数都小于n,其中n=
。如果在输入文件中有任何整数重复出现就是致命错误。没有其他数据与该整数相关联。
输出:按升序排列的输入整数的列表
约束:最多有(大约)1MB的内存空间可用,有充足的磁盘存储空间可用。运行时间最多几分钟,运行时间为10秒就不需要进一步优化了。
以上就是问题的描述规范,应包含输入、输出和约束。输入是对程序的输入进行了详细的描述,输出描述了程序运行完之后应该返回的结果应当是什么样子,约束描述了该问题解决过程中的各种限制条件,包含了各方面的约束。
开篇用这么一个问题加一段对话,引出了问题的描述规范,接下来开始解决问题之路。
作者一开始就说了一种方法,用基于磁盘的归并排序来解决该问题,基于磁盘的归并排序是一种外排序方法,为什么要采用外排序方法呢?因为该问题的约束条件中描述了输入文件中最多有1千万个不重复的7位数,内存的大小限制位1MB。在此先进行初步计算,1MB=1024*1024*8=8,388,608bit,采用平均计算后,8,388,608/10000000=0.84bit,即用0.84bit存储1个7位数,显然是不可能的。意味着使用1MB的内存,是无法读入1千万个的7位数,因此首要方法都会考虑用外排序。
我其实一开始并没有明白作者为什么一开始就想到了基于磁盘的归并排序,如果有跟我一样有问题的读者可以看以下外排序相关的介绍。
接着作者基于该问题的特殊性,又提出了该排序问题的另一种解法,是基于该问题的特殊性,思路如下:待排序的数不大于n,根据内存大小,将n个数分成m份,在第i次,读入范围在(i-1)*n/m~(i*n/m-1)的数到内存中,在内存中采用内排序算法将本次读入的所有元素进行排序,因为是对整数进行排序,快排是个非常不错的选择。但因此需要读入输入文件m次。到目前位置,作者已经提出了两种方案,第一种是基于磁盘的归并排序(外排序),第二种是根据该问题的特殊情况采用的一种特殊排序方法,但是需要多次读入输入文件,并进行多轮排序。
在以上两种方案之后,作者又提出了一种采用以上两种方法优点的方案,读入输入文件一次且不使用中间文件。这种方案一定要能够在1MB内存中可以表示文件中所有的整数。于是问题就变成了如何在800万个可用位表示最多1000万个互异的整数。
基于该排序问题的特殊性:待排序的数据限制在相对较小的范围内、数据没有重复、待排序的数据是单一的整数且没有任何其他关联数据,可采用位图或者位向量来解决该问题。位图的基本思想使用第i位是0或者1来表示对应的整数是否出现过,例如用一个4位长的位图,来表示1和3出现过,应是如下形式:0101,第1和3位都是1,表示1和3出现过。
针对上面的问题,可以构造一个长度为1000万个位的字符串,从输入文件中挨个读取输入,文件中的每个7位数都不会重复,每读入到一个7位数i,便将字符串中的第i位置为1,表示该数字出现了,进行排序的时候,从0开始遍历该字符串,出现1的数代表刚才输入文件中有对应的数值,遍历完该字符串也就是从小到大完成了排序,最后将排序结果输出到输出文件中,便完成了该问题所要求的排序结果。

2997

被折叠的 条评论
为什么被折叠?



