Algorithm
The algorithm linked in the post is a peculiar implementation of counting sort. I'm not sure why splicing and chaining is used, but I believe blunt counting into an array of three elements is much more effective. Your algorithm would be written in generic and comprehensive manner, as int
s are always copy constructible.
Code
As mentioned in the comments, this is ANSI C. I've actually tried it on ideone and it worked perfectly. I also established that your code works correctly for small inputs (haven't tested others). I'm not sure if reimplementing linked list is part of the task, but I'll suggest you standard library alternatives for the code you've written:
Reading:
Not everybody might agree with me, but I like this usage:
unsigned int size{};
std::cin >> size;
std::list<int> values;
std::copy_n(std::istream_iterator<int>{std::cin}, size,
std::back_inserter(values));
As you can see, everything is handled for us. Starting from looping size
times, to push_back
ing items in. It is better to write declarative code that is also efficient. It is not always possible, but this case is easy enough to be handled by declarative code.
printing:
Here is what people usually prefer:
for (int value : values)
std::cout << value << ' ';
std::cout << '\n';
But I prefer:
std::copy(values.begin(), values.end(),
std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
It is much harder to say which one is more expressive, thus this is more a matter of taste.
sorting:
C++ algorithms on containers are usually written using iterators. Do note though that sometimes iterators get in the way, like when you'll try to implement heap sort (e.g. heavy use of indices is needed). In this case, iterators are pretty good.
Lets start from the signature:
using iterator = std::list<int>::iterator;
void terse_sort(iterator start, iterator end)
Most of the range algorithms are declared like that: start
and end
of the range. Do note that end
is one past the end. Here is an example: if your range is {1, 0, 1, 2}
, start
will point to leftmost 1, and end
will point to "uncertainty" after 2
. With that out of the way, lets get to the most interesting part of the algorithm:
counting:
As name of the algorithm implies, use counting:
unsigned int occurence_count[3] = {0};
auto count_pos = start;
while (count_pos != end)
++occurence_count[*count_pos++];
Thw while loop is the same as (p != NULL)
in the code. For every index 0, 1, 2, occurence_count
will tell how many of those occured in the list. Now, lets put the values in the correct places:
for (unsigned int value = 0; value < 3; ++value)
for (unsigned int i = 0; i < occurence_count[value]; ++i)
*start++ = value;
As it can be observed, for index 0, it is gonna write 0 from the start, occurence_count[0]
times. The same for indices 1 and 2.
The algorithm mentioned in the linked post
I actually thought about it a little. The thing is that it doesn't perform any swaps! So, if blunt counting sort performs copies (e.g. doesn't regard cases where key!=value
), the mentioned algorithm preserves relative order and regards keys not being the same as value. I found it really peculiar, thus decided to implement somewhat more generic version:
#include <map>
template <template <typename ...> typename List, typename ValueType, typename ... Rest>
void splice_sort(List<ValueType, Rest...>& list)
{
std::map<ValueType, List<ValueType>> locomotives;
while (list.size() != 0)
{
auto locomotive_end = locomotives[list.front()].cend();
locomotives[list.front()].splice(locomotive_end, list, list.begin());
}
//no elements left in list,
//put it back in the correct order
for (auto&& locomotive : locomotives)
list.splice(list.cend(), locomotive.second);
}
Since the algorithm is designed for types that are heavy to swap, they are probably not int
s, but std::array
s or some other weird swappable type.
A bit of trivia.
Why locomotive
?

By Marcus Wong Wongm [GFDL (http://www.gnu.org/copyleft/fdl.html) or CC BY-SA 4.0 (https://creativecommons.org/licenses/by-sa/4.0)], from Wikimedia Commons
In countries of post-Soviet era, the trains led by locomotives are commonplace, especially just after the collapse of Soviet union. When reading about the algorithm, I recalled my travels on those when I was a kid. The algorithms behaves like merge of trains: trains with lower value are appended to ones which have next bigger value, and so on until all trains are merged into one.
Full code to play with
#include <map>
template <template <typename ...> typename List, typename ValueType, typename ... Rest>
void splice_sort(List<ValueType, Rest...>& list)
{
std::map<ValueType, List<ValueType>> locomotives;
while (list.size() != 0)
{
auto locomotive_end = locomotives[list.front()].cend();
locomotives[list.front()].splice(locomotive_end, list, list.begin());
}
//no elements left in list,
//put it back in the correct order
for (auto&& locomotive : locomotives)
list.splice(list.cend(), locomotive.second);
}
#include <list>
using iterator = std::list<int>::iterator;
void terse_sort(iterator start, iterator end)
{
unsigned int occurence_count[3] = { 0 };
auto count_pos = start;
while (count_pos != end)
++occurence_count[*count_pos++];
for (unsigned int value = 0; value < 3; ++value)
for (unsigned int i = 0; i < occurence_count[value]; ++i)
*start++ = value;
}
#include <iostream>
#include <algorithm>
#include <iterator>
#include <sstream>
void terse_sort_demo(std::istream& in)
{
unsigned int size{};
in >> size;
std::list<int> values;
std::copy_n(std::istream_iterator<int>{in}, size, std::back_inserter(values));
std::copy(values.begin(), values.end(),
std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
terse_sort(values.begin(), values.end());
std::copy(values.begin(), values.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
}
void splice_sort_demo(std::istream& in)
{
unsigned int size{};
in >> size;
std::list<int> values;
std::copy_n(std::istream_iterator<int>{in}, size, std::back_inserter(values));
std::copy(values.begin(), values.end(),
std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
splice_sort(values);
std::copy(values.begin(), values.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
}
int main()
{
const std::string input = "6\n"
"2 0 0 2 1 1\n";
std::istringstream input_stream(input);
std::cout << "splice sort:\n";
splice_sort_demo(input_stream);
std::cout << "\n\n";
input_stream.str(input);
std::cout << "raw counting sort:\n";
terse_sort_demo(input_stream);
}
Demo on Wandbox.