Skip to content

Bottom K Selection

Select the k smallest elements using bounded structures or partitioning.

Bottom K Selection returns the kk smallest elements of an array. It is symmetric to Top K selection and can be implemented with heaps or partition based methods.

The result may be unordered unless explicitly sorted.

Problem

Given an array AA of length nn and an integer kk, return the kk smallest elements.

Algorithm

Heap Based

Maintain a max heap of size kk.

bottom_k_heap(A, k):
    H = empty max heap

    for x in A:
        if size(H) < k:
            push x into H
        else if x < maximum(H):
            pop maximum from H
            push x into H

    return elements of H

Partition Based

Use selection to isolate the smallest region.

bottom_k_partition(A, k):
    nth_element(A, k)
    return A[0..k-1]

Example

Let

A=[7,2,9,4,3] A = [7, 2, 9, 4, 3]

and

k=3 k = 3

The smallest elements are:

[2,3,4] [2, 3, 4]

Partition result may look like:

[2,3,4,9,7] [2, 3, 4, 9, 7]

Return the first kk elements.

Correctness

For the heap method, the structure always contains the smallest kk elements seen so far. Larger elements are discarded.

For the partition method, Nth Element ensures that all elements before index kk are less than or equal to A[k]A[k], so they form the correct subset.

Complexity

methodtime
heapO(nlogk)O(n \log k)
partition averageO(n)O(n)
partition worstO(n2)O(n^2)

Sorting the result adds:

O(klogk) O(k \log k)

Space:

  • heap: O(k)O(k)
  • partition: O(1)O(1)

When to Use

Use Bottom K Selection when:

  • you need smallest values
  • knk \ll n
  • streaming or memory limits apply

Partition based methods are faster for in-memory data, while heap based methods are more flexible for streaming.

Implementation

import heapq

def bottom_k_heap(a, k):
    if k <= 0:
        return []

    heap = []

    for x in a:
        if len(heap) < k:
            heapq.heappush(heap, -x)
        elif x < -heap[0]:
            heapq.heapreplace(heap, -x)

    return [-x for x in heap]
import random

def bottom_k_partition(a, k):
    def partition(left, right, pivot_index):
        pivot = a[pivot_index]
        a[pivot_index], a[right] = a[right], a[pivot_index]
        store = left
        for i in range(left, right):
            if a[i] < pivot:
                a[store], a[i] = a[i], a[store]
                store += 1
        a[right], a[store] = a[store], a[right]
        return store

    def nth_element(left, right, k):
        while True:
            if left == right:
                return

            pivot_index = random.randint(left, right)
            pivot_index = partition(left, right, pivot_index)

            if pivot_index == k:
                return
            elif k < pivot_index:
                right = pivot_index - 1
            else:
                left = pivot_index + 1

    nth_element(0, len(a) - 1, k)
    return a[:k]