Skip to content

Stable Sorting

Introduction

Python's Timsort is a stable sorting algorithm.

In a stable sort, if two elements have the exact same sorting key, their original relative order is preserved in the sorted output.

Understanding stability allows you to perform complex multi-pass sorts effortlessly.


What is Stability?

Imagine sorting a list of files by their size.

files = [
    ("a.txt", 100),
    ("b.txt", 50),
    ("c.txt", 100)
]

# Sort by size (index 1)
files.sort(key=lambda x: x[1])

Because 100 == 100, a.txt and c.txt tie. In an unstable sort (like Quick Sort), c.txt might randomly end up before a.txt. In a stable sort (like Python's Timsort), because a.txt came before c.txt in the original array, it is guaranteed to come before c.txt in the sorted array.


The Multi-Pass Sort Trick

Suppose an interview question asks you to sort an array of logs. - Primary condition: Sort by User ID (Ascending). - Secondary condition: Sort by Action Name (Descending).

Because Action Name is a string, you cannot easily negate it in a tuple key=lambda x: (x.id, -x.action).

Instead, you can leverage stability by sorting multiple times, starting with the least important condition and ending with the most important condition.

logs = [
    {"id": 2, "action": "LOGIN"},
    {"id": 1, "action": "LOGOUT"},
    {"id": 2, "action": "CLICK"}
]

# 1. Sort by Secondary Condition first (Action Descending)
logs.sort(key=lambda x: x["action"], reverse=True)

# 2. Sort by Primary Condition last (ID Ascending)
# Because it is a stable sort, ties in ID will preserve 
# the descending Action order established in Step 1!
logs.sort(key=lambda x: x["id"])

for log in logs:
    print(log)

# {'id': 1, 'action': 'LOGOUT'}
# {'id': 2, 'action': 'LOGIN'}
# {'id': 2, 'action': 'CLICK'}

Time Complexity of Multi-Pass

Running \(O(N \log N)\) twice is still \(O(N \log N)\). Furthermore, because Timsort is heavily optimized for partially sorted data, the second sort pass runs incredibly fast.


Summary

  • Python's sort is stable (preserves original order on ties).
  • If you have complex multi-criteria sorting (especially mixing ascending and descending strings), sort multiple times.
  • Always sort from least important criteria to most important criteria.