Just realized I have never implemented Radix Sort (spurred about watching a YouTube video about it). So I thought I would give it a go.
This is Radix Sort, using a counting implementation. For numbers that are N
bytes in length, we use an N
pass counting approach. Starting with the least significant byte we do a counting sort of all the values. We then repeat the counting sort using each consecutively higher byte in the values as the index.
Only have access to C++17 (so added some view objects (that I believe are available in C++20).
Comment on anything appreciated.
Suggestions on how I could remove the final std::move()
to remove that copy would be nice. If I was not using iterators, that would be trivial. I am sure I can do it with some thought.
Views
#include <iostream>
#include <vector>
#include <array>
// A normal and reverse view created from Iterators.
// Makes using the Range based `for()` easier rather than having
// to manually work with the iterators themselves.
template<typename I>
struct View
{
I beginRange;
I endRange;
View(I beginRange, I endRange)
: beginRange(beginRange)
, endRange(endRange)
{}
using iterator = I;
iterator begin() {return beginRange;}
iterator end() {return endRange;}
};
template<typename I>
struct ReverseView
{
I beginRange;
I endRange;
ReverseView(I beginRange, I endRange)
: beginRange(beginRange)
, endRange(endRange)
{}
using iterator = std::reverse_iterator<I>;
iterator begin() {return iterator(endRange);}
iterator end() {return iterator(beginRange);}
};
Radix Sort
template<typename I>
void rsort(I begin, I end)
{
// Create an intermediate output container.
using Type = typename std::iterator_traits<I>::value_type;
std::vector<Type> output(std::distance(begin, end));
// A loop to loop over each byte as the radix index.
for (int index = 0; index < sizeof(Type); ++index) {
// Keep track of counts for each radix value.
// Since we are using bytes we need 256 values 0->255
std::array<std::uint64_t, 0x100> count{};
// Step 1: Count each radix value.
// Complexity O(n)
for (auto const& value: View(begin, end)) {
++count[ (value >> (index * 8)) & 0xFF ];
}
// Step 2: Calculate prefix value.
// Effectively if we do a count sort this is the
// one past the end of the range for this value
// of radix to be stored.
// Complexity O(1) assuming 255 is small compared to n
std::uint64_t sum = 0;
for (auto& c: count) {
sum = sum + c;
c = sum;
}
// Step 3: Copy from the input to output by
// using the radex index (do in reverse order).
// Complexity O(n)
for (auto const& value: ReverseView(begin, end)) {
// Offset starts at one past the end of the
// range for this radix value.
std::uint64_t& offset = count[ (value >> (index * 8)) & 0xFF ];
// So subtract one before using.
// It now becomes one past the end for the remaining
// values in the input array for this count.
--offset;
output[offset] = value;
}
// Step 4: Copy output back to the input.
// I am sure I can eliminate this step with some thought.
// Complexity O(n)
std::move(std::begin(output), std::end(output), begin);
}
}
So Complexity is O(4 * (3n + 1))
=> O(12n + 4)
=> O(n)
.
A test harness:
int main()
{
std::vector<int> data{4583,182,5433,8092,11465,6614,29731,24061,29432,24542,32685,9724,31005,456,29255,25325,30048,18875,27775,30360,13531,1029,5715,3729,31680,22998,2359,29525,15433,7106,20196,11561,20578,14325,231,6835,8729,22579,21733,21845,28353,25495,8623,32589,4329,24583,10505,22413,32000,28538,12858,12123,23749,25833,16723,7593,30064,11542,30528,3122,17570,1792,26256,31321,13,18465,26884,4544,25571,10349,16857,26795,31744,21003,6357,3603,21462,12498,6675,15242,14620,15746,18063,18642,32054,16583,16753,15675,24931,18926};
rsort(std::begin(data), std::end(data));
std::cout << "[ ";
char sep = ' ';
for (auto const& v: data) {
std::cout << sep << " " << v;
sep = ',';
}
std::cout << " ]\n";
}
The result:
[ 13, 182, 231, 456, 1029, 1792, 2359, 3122, 3603, 3729, 4329, 4544, 4583, 5433, 5715, 6357, 6614, 6675, 6835, 7106, 7593, 8092, 8623, 8729, 9724, 10349, 10505, 11465, 11542, 11561, 12123, 12498, 12858, 13531, 14325, 14620, 15242, 15433, 15675, 15746, 16583, 16723, 16753, 16857, 17570, 18063, 18465, 18642, 18875, 18926, 20196, 20578, 21003, 21462, 21733, 21845, 22413, 22579, 22998, 23749, 24061, 24542, 24583, 24931, 25325, 25495, 25571, 25833, 26256, 26795, 26884, 27775, 28353, 28538, 29255, 29432, 29525, 29731, 30048, 30064, 30360, 30528, 31005, 31321, 31680, 31744, 32000, 32054, 32589, 32685 ]