It may seem a bit useless talking about some sorting algorithm at this point in computer history, and specially about one that dates back as far as 1887. Some people consider that it is enough to know what the sorting routine in your language/library of choice is, but I consider that:
- It’s definitely good to have a breadth knowledge about different topics in Computer Science, and sorting is a basic field in algorithmics.
- Radix sort is not useful sometimes, but it can give you a performance boost in some other circumstances.
- It may seem that all programmers already know how this works. I am sure that not only most of them don’t know how it works, but that there are many Computer Scientists that don’t even know of its existence. I myself did not realize of its existence until I have spent almost all of my years at university.
So, what is radix sort about? It’s not like the basic and traditional sorting algorithms in the sense that no comparisons are made during its execution. Thanks to that, it can break the theoretical O(n log n) lower limit of a sorting algorithm. It’s time complexity is O(k*N), which makes it lineal. (k, in our implementation, will be the number of bytes of the elements to sort).
Let’s go to the point. Imagine first that we want to sort some bytes, for example:
5, 44, 200, 110 and 90
We have a first buffer of 256 integers that will act as a counter of how many bytes with that value are there to sort. We will call this the distribution buffer, because it stores how the bytes are distributed. In our case, all would be set to 0 except elements 5, 44, 90, 110 and 200 which would be set to 1. So, we traverse all the input elements incrementing the counters:
// source is an array with N elements to be sorted
unsigned int distribution[256];
for(unsigned int i=0; i < 256; i++)
distribution[i] = 0;
for(unsigned int i=0; i < N; i++)
distribution[source[i]]++;
Once we know how many elements of each value are there, we use another buffer of 256 entries too, which we will call indices buffer, to accumulate the values from the distribution buffer. The entries at the indices buffer indicate at which position, once sorted, an element with a certain value must go. Here, elements until 5 would be 0, elements from, 6 to 44 would be 1, from 45 to 90 would be 2, and so on.
unsigned int indices[256];
for(unsigned int i=0; i<256; i++)
indices[i] = 0;
for(unsigned int i=1; i<256; i++)
indices[i] = indices[i-1]+distribution[i-1];
Finally, we iterate over the input elements again, and as we know at which position do they correspond when sorted, we can put them at their place in a new buffer called destination buffer, and which has the size of the input buffer. Once did this, we increment the index for that value as the next element with the same value (if there is another) must go not to the same entry but to the next one. If we want the sorted values to go in the source list, we copy them.
unsigned char destination[N];
for(unsigned int i=0; i < N; i++)
destination[ indices[source[i]]++ ] = source[i];
for(unsigned int i=0; i < N; i++)
source[i] = destination[i];
Well, you may say now, I do not want to sort only bytes, I want to sort, for example, 32 bits integers, and if I have to create a couple of buffers of 232 elements each this algorithm is not going to be very useful. That's right, but you don't have to do that. You can notice that due to the way the elements are iterated again looking for its corresponding sorted index, and then that index being incremented, this sort is stable (In short, a stable sort is one which, for elements with the same sorting value, the output order is the same as the input).
So, as the the algorithm is stable we can sort larger types without the need of larger distribution and indices buffers. All what we are going to do is sort first taking into account only the least significant byte, then sorting by the next more significant byte, until we have sorted the elements by the most significant byte, at which point the elements are completely sorted.
In fact, and by what we have seem, it may seem that only unsigned integers can be sorted by radix. But negative and even floating point numbers can be sorted with radix sort, as exposed by Pierre Terdiman.
If you are interested in the topic, more tricks and optimization techniques can be found in this article by Michael Herf.
Algorithms, Optimization algorithm, Optimization, radix, sorting