Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Coding Interview Patterns  Module 27 – Greedy algorithms and proofs

Least-cost merges with a heap (Huffman)

Merge files or ropes at the least total cost: the problem and its limits, a brute force over every merge order, and the signal that ties it to Huffman codes.

  • Intermediate
  • 35 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free intro, Pro: 8 of 35 minutes free
Free: Merging sorted log files · The brute force, and why it fails at these sizes · What to notice. With a pass: the other 12 sections and 3 interview answers, about 27 minutes.

What you will learn

  • Merge items two at a time at the least total cost with a min-heap
  • Prove that merging the two smallest first is optimal with an exchange argument
  • Merge sorted sizes in O(n) with two queues instead of a heap
  • Build a Huffman code from the same merges, give out its codes from lengths alone, and check it is prefix-free
  • Extend the method to merges of k files at once and to merges of neighbouring files only

Before you start

What you’ll learn · Before you start

What you will learn

  • Merge items two at a time at the least total cost with a min-heap
  • Prove that merging the two smallest first is optimal with an exchange argument
  • Merge sorted sizes in O(n) with two queues instead of a heap
  • Build a Huffman code from the same merges, give out its codes from lengths alone, and check it is prefix-free
  • Extend the method to merges of k files at once and to merges of neighbouring files only

Before you start

On this page

To merge files, or join ropes, at the least total cost when each merge costs the two sizes added together, always merge the two smallest that are left. A min-heap hands back the two smallest in O(log n) after every merge, so n items take O(n log n). The rule is the one David Huffman published in 1952 for codes with the least redundancy: with letter counts as the sizes, the merges build the code tree, and the total cost is the length of the coded text in bits. The problem is often called the minimum cost of ropes; here it is told with sorted log files.

Merging sorted log files

The problem

A server keeps n sorted log files of s[0], …, s[n − 1] megabytes. Merging two sorted files into one sorted file reads and writes every line of both, so it costs the sum of their sizes and gives a file of that size, which can be merged again. Merge everything into one file at the least total cost. Joining ropes is the same problem: joining two ropes costs the sum of their lengths. Limits: 1 ≤ n ≤ 10⁵ and 1 ≤ s[i] ≤ 10⁹.

  • [20, 4, 7, 9, 3] costs 87 at least: merge 3 and 4, then the two 7s, then 9 with 14 and last 20 with 23, for 7 + 14 + 23 + 43. Merging in the order given costs 138.
  • [5, 5, 5, 5] costs 40: two pairs, then the two halves, for 10 + 10 + 20. Adding one file at a time to a growing file costs 45.
  • [12] costs 0, because there is nothing to merge.

The brute force, and why it fails at these sizes

Try every pair at every step and keep the cheapest total. With k files left there are k(k − 1)/2 pairs to choose from, so n files have n!(n − 1)!/2ⁿ⁻¹ merge orders: 180 for the five files above, 2,571,912,000 for ten, and about 10^883,039 for 100,000.

Every merge order, with the orders counted Python · brute/brute.py
# The brute force for merging files: at every step try every pair of the files that are left, merge it, and go on;
# keep the cheapest total. It is right by construction, and it counts the merge orders it tries.
from math import factorial, lgamma, log


def cheapest(sizes):
    """(the least total cost, the number of merge orders tried)"""
    if len(sizes) <= 1:
        return 0, 1
    best, orders = None, 0
    for i in range(len(sizes)):
        for j in range(i + 1, len(sizes)):
            rest = sizes[:i] + sizes[i + 1 : j] + sizes[j + 1 :] + [sizes[i] + sizes[j]]
            cost, tried = cheapest(rest)
            cost += sizes[i] + sizes[j]
            orders += tried
            best = cost if best is None else min(best, cost)
    return best, orders


def in_given_order(sizes):
    """Merge the first two files, then the result with the next file, and so on."""
    total, current = 0, sizes[0]
    for s in sizes[1:]:
        current += s
        total += current
    return total


for sizes in ([20, 4, 7, 9, 3], [5, 5, 5, 5], [12]):
    best, orders = cheapest(sizes)
    print(f"{sizes}: least cost {best} ({orders:,} merge order{'s' * (orders != 1)} tried); merged in the given order: {in_given_order(sizes)}")
for n in (10, 20):
    print(f"{n} files: {factorial(n) * factorial(n - 1) // 2 ** (n - 1):,} merge orders")  # C(n, 2) * C(n-1, 2) * ... * 1
n = 100_000
digits = (lgamma(n + 1) + lgamma(n) - (n - 1) * log(2)) / log(10)
print(f"{n:,} files: about 10^{digits:,.0f} merge orders")

Output

[20, 4, 7, 9, 3]: least cost 87 (180 merge orders tried); merged in the given order: 138
[5, 5, 5, 5]: least cost 40 (18 merge orders tried); merged in the given order: 45
[12]: least cost 0 (1 merge order tried); merged in the given order: 0
10 files: 2,571,912,000 merge orders
20 files: 564,480,989,588,730,591,336,960,000,000 merge orders
100,000 files: about 10^883,039 merge orders

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 brute.py

What to notice

A merge tree: files 3 and 4 merge into 7, which merges with 7 into 14, then with 9 into 23, then with 20 into 43, the final file.3479207142343

A file is paid once per merge above it

Text description of the diagram

The diagram is a tree drawn from the bottom up. Squares are the five files of the first example, of 3, 4, 7, 9 and 20 MB; circles are the files the merges make. Arrows lead from the two parts of each merge to the file it makes.

  1. The 3 MB and 4 MB files merge into a 7 MB file.
  2. That file and the 7 MB file of the input merge into 14 MB.
  3. The 14 MB file and the 9 MB file merge into 23 MB.
  4. The 23 MB file and the 20 MB file merge into the final 43 MB file, drawn at the top with a double border.

Counting the merges above each square gives how often that file is copied: 4 times for the 3 MB and 4 MB files, 3 for the 7 MB file, 2 for the 9 MB file and once for the 20 MB file. The total cost is each size times that count, 3 × 4 + 4 × 4 + 7 × 3 + 9 × 2 + 20 × 1 = 87, the same as adding the four merges, 7 + 14 + 23 + 43.

  • Every merge copies its two files once more. A file is paid for once for every merge above it in the tree, so the total is each size times the number of merges it goes through.
  • So small files belong deep and large files high. Merging the two smallest first sends them through the most merges, and the largest file joins last, through one.
  • The merged file joins the others. After every merge the two smallest have to be found again, among the files left and the new one.

A min-heap finds them in O(log n), for O(n log n) in all. When the sizes arrive sorted, two queues give the same total in O(n). Huffman codes come from the same merges, with letter counts as the sizes.

The heap, step by step Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • Why merging the two smallest is optimal: a proof
  • A dry run of the heap
  • Sorted sizes: two queues and O(n)
  • Huffman codes from the same merges
    • Codes from lengths alone, as DEFLATE builds them
  • The code in five languages
    • Merged sizes past 32 bits
  • Edge cases, and five bugs that a search exposes
  • Variants: k files at once, and neighbours only
  • Exercise: the cheapest merges, then the lengths of a Huffman code
    • The cheapest merges, then the lengths of a Huffman code
  • A practice set with sample answers
  • Check yourself
  • Interview questions
    • When merging files two at a time costs the two sizes added, why should the two smallest files be merged first?
    • Prove that repeatedly merging the two smallest items gives the least total cost. Which property of merge trees does your proof rely on, and where would it break if a merge could take three items?
    • Ten million file sizes arrive already sorted. Can you compute the least merge cost faster than with a heap? Give the method, why it is correct, and what you would measure to show it is faster.
    • Log chunks must stay in time order, so only neighbouring chunks may be merged. A colleague merges the cheapest neighbouring pair each time. Is that optimal? If not, give a counterexample and a correct method with its cost.
  • Key takeaways

10 runnable examples (C++, Go, Java, JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 24 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.