Order After K Digit Passes
Problem
A radix sort on whole numbers that are zero or more works one decimal digit at a time, starting from the ones digit. Each pass stably regroups the list by the current digit, 0 first and 9 last, keeping the existing order inside each group. Given a list and a number k, return the list exactly as it looks after the first k passes.
Examples
Input: nums = [53, 7, 318, 90, 41, 206, 17], k = 1
Output: [90, 41, 53, 206, 7, 17, 318]
Why: grouped by ones digit: 0, 1, 3, 6, 7, 7, 8; 7 stays ahead of 17
Input: nums = [53, 7, 318, 90, 41, 206, 17], k = 2
Output: [206, 7, 17, 318, 41, 53, 90]
Why: the second pass regroups the first pass's output by the tens digit
Input: nums = [30, 3, 300], k = 0
Output: [30, 3, 300]
Why: edge case, no passes, so nothing moves
Hints
0 / 3
Sorting by the full value throws away exactly what the question asks about: the order in the middle of the process.
A single pass is a bucket distribution: ten buckets for the ten digits, filled in list order and then read back in bucket order. Filling in list order is what makes the pass stable.
Repeat k times with a place value that starts at 1 and is multiplied by 10 after each pass. The digit of x for the current pass is x divided by the place value, rounded down, then taken modulo 10.
Solution
Each pass drops every number into one of ten buckets by its current digit, in the order the list already has, then concatenates the buckets from 0 to 9. Because the buckets are filled in list order, numbers with the same digit keep their earlier relative order, which is the stability that makes the later passes build on the earlier ones. After k passes the list is ordered by its last k digits, ties in original order. Time is O(k(n + 10)), and space is O(n) for the buckets.
def after_passes(nums, k):
order, place = list(nums), 1
for _ in range(k):
buckets = [[] for _ in range(10)]
for x in order: # list order in, so the pass is stable
buckets[x // place % 10].append(x)
order = [x for b in buckets for x in b] # read back digit 0 to 9
place *= 10
return order
print(after_passes([53, 7, 318, 90, 41, 206, 17], 1)) # -> [90, 41, 53, 206, 7, 17, 318]
print(after_passes([53, 7, 318, 90, 41, 206, 17], 2)) # -> [206, 7, 17, 318, 41, 53, 90]
print(after_passes([30, 3, 300], 0)) # -> [30, 3, 300]Stuck on the idea rather than the code? Radix Sort covers it.