开始读《编程珠玑》,刚看完开篇就觉得很开心。
作者从“A Friendly Conversation”讲起,大概是这样子的:
一个programmer问作者:“How do I sort a disk file?”
于是作者简要介绍了一下如何在磁盘中实现Merge Sort,并建议他先钻研下算法课本。然而programmer关注的是如何解决问题,而不是进一步学习。流行的磁盘排序程序大约10多个函数,200行程序代码,实现和测试这部分代码估计不超过1星期。
进一步的对话中,作者了解到,排序的文件包含至多10000000个记录,每条记录都是一个7位整数,数字不重复。而且至多只有1MB的可用主存。
于是问题开始清晰了,因为是整数排序,所有需要的代码量相应减少,执行速度也快了很多,但是调试仍需要一定时间。
第二个方案则是,用40个通道依次将整数读到主存中并排序输出(用32位整数表示每个号码,1MB空间可以存储250000个号码。)在主存中,快排效率是很高的,而且代码简单。
对比上述两种方案,第一种从输入中一次性读取文件,然后在工作文件的辅助下进行排序(工作文件会进行多次读取与写入操作),之后对文件一次性输出。
第二种则需要多次读取输入文件,不使用中间文件。只进行一次输出文件操作。
是否有第三种方法,能够结合以上两者的优势->一次性读取,不使用中间文件呢?
只有当输入文件的所有整数都可以存放(或者说,表示)在可用的主存空间内时,我们才可以实现第三种。
于是问题变为:如何将至多一千万个不同整数表示在大约八百万可用的比特中。
位图
简单的来讲,我们可以使用一个20位的字符串来表示一个小于20的非负整数集合,例如集合{1,2,3,5,8,13}表示为:
0 1 1 1 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0
由于7位十进制数表示了一个小于千万的数字,所以我们可以使用一个具有一千万位的字符串表示该文件,当且仅当整数i在该文件中,第i个位才设为1。
算法:
for i = [0,n)
bit[i] = 0
for each i in the input file
bit[i] = 1
for i = [0,n)
if bit[i] == 1
write i on the output file
位图局限性:
输入范围相对要小一些,不能包含重复数据,没有数据与整数记录相关联。
感言
从最初的磁盘归并排序,到主存的快速排序,再到位图,最后使用位图这一点让我眼前一亮。前两种方法一般情况下都很容易想到,但是其简洁程度远比不上位图的解决方法。从本例中也看出,对问题的描述和定义是如此的重要。如作者所说:“Defining the problem was about ninety percent of this battle!”
如果没有后面的对话,问题仅仅是“How do I sort a disk file?”这无疑是绕了许多弯路。
位图数据结构有其局限性,也有其奇妙之处,我们需要做的是在适当的问题中选取适当的数据结构,灵活应对~