C 语言最优归并模式

2025年1月7日 | 阅读 6 分钟

最优合并模式问题是在文件管理系统中合并多个已排序文件时出现的一个著名的算法问题。本文提出了一种算法,重点介绍了如何最优地合并给定的一组不同大小的文件。

由于合并取决于两个文件的总大小,而目标是最小化合并所有文件的总成本,因此考虑拥有几个包含一定数量记录的文件。之后,当合并任意两个文件时,工作量将取决于这些文件的总大小。如果选择了错误的合并顺序,则结果成本可能会高得多。因此,找到产生此成本的最优合并序列变得很重要。

让我们以三个大小分别为 10、20 和 30 单位的文件为例。假设我们以某种非最优方式合并文件,首先以 50 的成本合并 20 和 30 大小的文件,然后将该结果与 10 大小的文件合并。之后,总成本将为 50 + 60 = 110。但是,如果我们首先以 30 的成本合并 10 和 20 大小的文件,然后将该结果与 30 大小的文件合并,则总成本将为 30 + 60 = 90。这个简单的例子说明了不同合并序列之间的成本差异,并激发了对最优方法的需要。

所有合并操作的总和定义了成本。如上所述,该问题在需要执行大量合并时具有实际应用,例如在外部排序算法和文件处理可能涉及合并的操作系统中。如果使用了最优合并模式,我们可以确保这些操作将以最小的计算开销完成。

程序

让我们举一个例子来说明 C 语言中的最优合并模式。

输出

 
Before merging:
File sizes: 5 10 15 20 25 30 35 
Optimal merge cost: 370   

说明

在此示例中,下面的代码使用最小堆解决了最优合并模式问题。通过合并不同大小的文件,有效地降低了总体合并成本。目标是合并文件,以最小化与文件大小总和成比例的计算成本。

这个问题可以通过贪婪算法最有效地解决。它通过始终先合并两个最小的文件来工作,这在每一步都能确保成本的增加尽可能小。它类似于构建用于数据压缩的霍夫曼树。实际解决方案通常会基于最小堆或优先队列来高效地完成此操作。它将所有文件大小插入堆中,并反复取出两个最小的文件,合并它们,然后将结果放回堆中。重复此过程,直到堆中只剩下一个文件。

文件结构:定义了一个文件结构来存储每个文件的大小。这为将来增加文件的更多属性提供了灵活性。

堆函数

  • heapify:文件数组维护堆属性,其中最小元素位于根。算法在发生扰动时重新排列堆。
  • extractMin:它从堆中删除并返回最小的文件,即大小最小的文件。
  • insertHeap:它将新合并的文件放回堆中,同时维护最小堆属性。
  • buildMinHeap:它将一个未排序的数组转换为最小堆,以便我们可以高效地提取文件的最小尺寸。
  • 最优合并成本计算:核心算法是 calculateOptimalMergeCost 函数。它从堆中取出两个最小的文件,合并它们,并将结果重新插入堆中。这个过程会一直重复,直到只剩下一个文件,每次合并一对文件时都会累积合并成本。

辅助函数

  • initializeFiles 设置文件大小。
  • printFileSizes 可视化过程。

复杂度分析

时间复杂度

  • 堆操作:创建初始堆需要 O(n) 时间,其中 n = 文件数。
  • 合并过程:由于最小堆的提取和插入由于其堆结构的维护而需要 O(log n) 时间,并且有 n-1 次合并,因此代码的总时间复杂度为 O(n log n)。这种效率归因于最小堆始终保证在每一步合并最小的两个文件。

空间复杂度

此代码的空间复杂度为O(n),因为用于存储文件大小的数组大小为 n。堆的底层结构需要相同的空间。一些用于存储文件大小和合并成本的辅助变量需要额外的常数空间,但总体空间使用变为 O(n)。