代码改变世界

合并小文件——一个算法在实际程序开发中应用的例子

2008-08-29 13:24  JimLiu  阅读(3620)  评论(14编辑  收藏  举报

现在的学生爱说算法对实际项目开发没多大作用,其实这是错误的——现在就园子里看到的一个讨论,提出一个我的方案,作为为一部分朋友举例说明“程序=数据结构+算法”这个不变的道理

讨论源于此http://space.cnblogs.com/question/2616/

讨论中,lexus朋友还是打算将小文件选择性合并,于是有了新讨论http://space.cnblogs.com/question/2630/

简单说一下算法的要求:当两个文件的大小(KB)小于某个设定值时,两个文件就合并,这样一直合并下去。

有朋友提出用排序来解决,我觉得开销太大,排序一次时间O(nlogn),如果每次都排序则复杂度高达O((n^2)logn),不值。

也有朋友提出使用简单插入,那么复杂度是O(n^2)。

“合并文件”,让我想起一道经典的ACM题目——“合并果子”,合并两个文件可以采用贪心,类似Huffman算法,这里合并多个文件和合并两个文件是一样的。

传统Huffman算法的时间复杂度是O(n^2),技巧不好的话会更多,通常的改进办法是使用堆进行优化。

关于堆(Heap),不了解的朋友可以参考一下数据结构的基础书籍

多余背景资料我在这里就不多做介绍了,下面说下我对这个问题的解决方案:

注:下文中每次对堆进行操作后都进行堆维护,不再赘述。

0.将所有文件建立小顶堆,并且建立一个足够大的缓冲区;

1.设缓冲区内文件总大小是buffer,堆顶文件大小是top;

2.查看top

    
if (buffer + top <= max_file_size) 则将堆顶弹出到缓冲区

    
else 将缓冲区内文件合并,新文件压入堆中

3.循环2,直到缓冲区为空,且top >= max_file_size

 

实际上这里的堆就是个优先队列(Priority Queue),STL中的PQ就是用堆实现的。

分析一下整个算法的性能:

文件总数为n

建立堆需要O(nlogn)

遍历所有文件需要O(n),这个过程中需要进行堆维护,每次O(logn)

所以总的时间复杂度是O(nlogn),和用堆改进的Huffman算法是一样的。

再看看空间需求:

直接算最坏的需求,如果所有文件都足够小,能把所有文件合并在一起,那么缓冲区需要n的长度,所以最坏需要n辅助空间。

以上是我自己能想到最快的算法。

从这个例子看出,在实际应用中,数据结构和算法依然占据核心的地位不会动摇,打好了基础才能做出好的程序。

面对STL,面对java,面对.NET Framework Library,这些琳琅满目的库,我们可以方便地应用各种数据结构和算法,但是如果我们对他们本身的实现没有一定的了解,有怎么能知道在不同的场合下什么选择才合适呢?

所以我还是坚信“程序=数据结构+算法”这个道理是恒定不变的真理。