Skip to content

Tournament Sort

Sort by repeatedly selecting the minimum element using a tournament tree.

Tournament sort selects elements by organizing comparisons as a tournament. Each leaf represents an input value. Internal nodes store the winner of comparing their children. For ascending order, the winner is the smaller value.

After the minimum is emitted, its leaf is replaced by infinity, and the path from that leaf to the root is recomputed. The next root gives the next smallest value.

Problem

Given a sequence AA of length nn, reorder it such that

A[0]A[1]A[n1] A[0] \le A[1] \le \cdots \le A[n-1]

Key Idea

A tournament tree lets the algorithm find the current minimum in O(1)O(1) after the tree is built. Updating the tree after removing one element costs O(logn)O(\log n).

Algorithm

tournament_sort(A):
    build tournament tree from A

    result = empty list

    repeat n times:
        winner = tree.root
        append winner.value to result

        replace winner.leaf with infinity
        update path from winner.leaf to root

    return result

Example

Let

A=[7,2,5,1] A = [7, 2, 5, 1]

Initial tournament:

        1
      /   \
     2     1
    / \   / \
   7   2 5   1

The first output is 11. Replace its leaf with infinity and recompute the affected path. The next root becomes 22, then 55, then 77.

Sorted result:

[1,2,5,7] [1, 2, 5, 7]

Correctness

The root of a min tournament tree always stores the minimum among all active leaves. After outputting that minimum, replacing its leaf with infinity removes exactly that element from future consideration. Recomputing the path to the root restores the tournament invariant. Repeating this process outputs the remaining elements in nondecreasing order.

Complexity

phasetime
build treeO(n)O(n)
each extractionO(logn)O(\log n)
all extractionsO(nlogn)O(n \log n)

Total time:

O(nlogn) O(n \log n)

Space complexity:

O(n) O(n)

Properties

  • comparison-based
  • not in-place
  • stable only with tie-breaking by original index
  • related to heapsort and loser-tree merge
  • useful when repeated minimum extraction is needed

When to Use

Tournament sort is useful when the sorting process naturally fits repeated selection, especially in external sorting and multiway merging. For in-memory array sorting, heapsort gives a more compact implementation with similar asymptotic behavior.

Implementation

import math

def tournament_sort(a):
    n = len(a)
    if n == 0:
        return []

    size = 1
    while size < n:
        size *= 2

    tree = [(math.inf, -1)] * (2 * size)

    for i, value in enumerate(a):
        tree[size + i] = (value, i)

    for i in range(size - 1, 0, -1):
        tree[i] = min(tree[2 * i], tree[2 * i + 1])

    result = []

    for _ in range(n):
        value, original_index = tree[1]
        result.append(value)

        pos = size + original_index
        tree[pos] = (math.inf, original_index)

        pos //= 2
        while pos >= 1:
            tree[pos] = min(tree[2 * pos], tree[2 * pos + 1])
            pos //= 2

    return result
type entry struct {
    value int
    index int
}

func less(a, b entry) bool {
    if a.value != b.value {
        return a.value < b.value
    }
    return a.index < b.index
}

func TournamentSort(a []int) []int {
    n := len(a)
    if n == 0 {
        return nil
    }

    size := 1
    for size < n {
        size *= 2
    }

    inf := int(^uint(0) >> 1)
    tree := make([]entry, 2*size)

    for i := range tree {
        tree[i] = entry{value: inf, index: i}
    }

    for i, v := range a {
        tree[size+i] = entry{value: v, index: i}
    }

    for i := size - 1; i >= 1; i-- {
        if less(tree[2*i], tree[2*i+1]) {
            tree[i] = tree[2*i]
        } else {
            tree[i] = tree[2*i+1]
        }
    }

    result := make([]int, 0, n)

    for i := 0; i < n; i++ {
        winner := tree[1]
        result = append(result, winner.value)

        pos := size + winner.index
        tree[pos] = entry{value: inf, index: winner.index}

        for pos /= 2; pos >= 1; pos /= 2 {
            if less(tree[2*pos], tree[2*pos+1]) {
                tree[pos] = tree[2*pos]
            } else {
                tree[pos] = tree[2*pos+1]
            }

            if pos == 1 {
                break
            }
        }
    }

    return result
}