Radix Sort
All sorting algorithms we've learnt so far use comparison. The lower bound number of comparison operations in all sorting algorithms is O(n log n)
. (This has to do with the limited amount of information obtained from a comparison: source.) You can't do better unless you sort without using comparison. This is where algorithms like radix sort come in.
Algorithms like radix sort fit into a group called integer sorts, where they sort by taking advantage of special properties found only in integers.
Note: You could convert strings into numerical representations and then sort those numbers. That's a workaround.
How It Works
Radix sort takes advantage of the fact that we know numbers with more digits must be larger. It doesn't matter what those numbers are either: 111 is larger than 99.
Using this fact, radix sort loops through a list and buckets the values by the ones place, turning it back into a list in the order it was bucketed. It then does the same for the tens place, hundreds place, and so on for all place values.
The number of loops performed depends on the largest number. So if 1591
is the largest number, 4 loops are performed.
Helper Functions
Radix sort requires a few helper functions in order to work:
getDigit
grabs number at specified place valuedigitCount
returns the number of digits in a numbermaxDigits
usesdigitCount
to return the number with the largest digits in a list
getDigit
// String version
const getDigit = (num, place) => {
const strNum = String(Math.abs(num)) // <= ignores negative values
const digit = strNum[strNum.length - place - 1]
if (digit === undefined) return 0
else return parseInt(digit)
}
getDigit(12345, 0) // 5
getDigit(12345, 5) // 0
// Number version
const getDigit = (num, place) => {
return Math.floor(Math.abs(num) / Math.pow(10, place)) % 10
}
digitCount
const digitCount = num => {
return String(Math.abs(num)).length
}
// OR
const digitCount = num => {
if (num === 0) return 1
return Math.floor(Math.log10(Math.abs(num))) + 1
}
mostDigits
const mostDigits = nums => {
let highestDigits = 0
for (let i = 0; i < nums.length; i++) {
const current = digitCount(nums[i])
if (current > highestDigits) {
highestDigits = current
}
}
return highestDigits
}
Implementation
The pseudocode goes like this:
Figure out how many digits the largest number has
Loop through the list as many times as the largest number of digits
For each loop, create buckets for digits 0 to 9 and add numbers into those buckets based on the digits at the target place value.
Merge the buckets, keeping the items in their current order.
const radixSort = nums => {
// 1. Get largest number's # of digits
const highestDigits = mostDigits(nums)
// 2. Loop through number of digits
for (let i = 0; i < highestDigits; i++) {
// 3a. Create buckets
const buckets = [[], [], [], [], [], [], [], [], [], []]
// 3b. Insert appropriate digit into appropriate bucket
for (let j = 0; j < nums.length; j++) {
const digit = getDigit(nums[j], i)
buckets[digit].push(nums[j])
}
// 4. Merge buckets
nums = []
for (let k = 0; k < buckets.length; k++) {
nums = nums.concat(buckets[k])
}
// THIS ALSO WORKS:
// nums = [].concat(...buckets)
}
return nums
}
Big O
Time complexity
The time complexity of radix sort in all cases is O(nk)
where k
is the largest number's number of digits--known as its word size.
However, k
can vary a lot, making radix sort's time complexity variable as well. For example, if every value in the array is distinct, k
ends up being log n
, making radix sort O(n log n)
, which is no better than the best sorting algorithms.
Space complexity
The space complexity of radix sort if O(n + k)
. I'm not sure why k
is there though...
Last updated