Stable Partition

Partition an array while preserving the relative order of elements inside each group.

Stable Partition

Stable partition rearranges an array so that elements satisfying a predicate appear before the others, while preserving relative order inside both groups.

You use it when grouping is required but original order still carries meaning.

Problem

Given an array $A$ of length $n$ and a predicate $P(x)$, reorder the array so that:

  • all elements satisfying $P$ appear first
  • all elements not satisfying $P$ appear after them
  • relative order inside each group is preserved

Algorithm

Use two temporary arrays. Store matching elements in one array and non-matching elements in another, then concatenate them.

stable_partition(A, P):
    yes = empty array
    no = empty array

    for x in A:
        if P(x):
            append x to yes
        else:
            append x to no

    k = 0
    for x in yes:
        A[k] = x
        k += 1

    for x in no:
        A[k] = x
        k += 1

    return length(yes)

Example

Let

$$ A = [8, 3, 5, 2, 1, 4] $$

Partition by even numbers:

$$ P(x): x \bmod 2 = 0 $$

pass element group
1 8 yes
2 3 no
3 5 no
4 2 yes
5 1 no
6 4 yes

The stable groups are:

$$ yes = [8, 2, 4] $$

and

$$ no = [3, 5, 1] $$

Final array:

$$ [8, 2, 4, 3, 5, 1] $$

Return partition index $3$.

Correctness

The algorithm scans elements from left to right. Each element that satisfies $P$ is appended to yes in its original order. Each remaining element is appended to no in its original order.

Writing all elements from yes before all elements from no produces a valid partition, and each group keeps the same relative order as in the input.

Complexity

operation time
scan input $O(n)$
write output $O(n)$
total $O(n)$

Space usage:

$$ O(n) $$

An in-place stable partition is possible, but it is more complex and often slower in practice.

When to Use

Stable partition is appropriate when:

  • elements must be grouped by a predicate
  • relative order matters
  • extra memory is acceptable
  • predictable linear behavior is preferred

It is less suitable when memory must remain constant and stability is not required.

Implementation

def stable_partition(a, predicate):
    yes = []
    no = []

    for x in a:
        if predicate(x):
            yes.append(x)
        else:
            no.append(x)

    k = 0
    for x in yes:
        a[k] = x
        k += 1

    for x in no:
        a[k] = x
        k += 1

    return len(yes)
func StablePartition[T any](a []T, pred func(T) bool) int {
    yes := make([]T, 0)
    no := make([]T, 0)

    for _, x := range a {
        if pred(x) {
            yes = append(yes, x)
        } else {
            no = append(no, x)
        }
    }

    k := 0
    for _, x := range yes {
        a[k] = x
        k++
    }

    for _, x := range no {
        a[k] = x
        k++
    }

    return len(yes)
}