I'm learning C++ and learning linked lists. I'm currently trying to make a radix sort for this type of lists, but my approach is not working so I was wondering if anyone could advice me on how to do it. Here's my code:
void simpleList::radixSort() {
for (int i = 0; i < numberOfDigits(); i++){
Node * tmp = firstNode;
for (int j = 0; j < counter(); j++){
int pow = 10;
for (int k = 0; k < 10; k++){
if (tmp -> data % pow == k){
insertFirst(tmp->data);
tmp = tmp -> next;
}
pow= pow * 10;
}
}
}
}
The function numberOfDigits() gets you the amount of digits on the max number of the list, and counter() the amount of elements in the list. Also insertFirst() puts the number at the beggining of the list.
A few notes to help you on your way:
Radix Sort
A radix sort is any kind of (stable n-ary bucket sort) repeated until you run out of digits in your sort keys. Make your life simple: use binary as your radix.
Keys
You are using
intvalues in your list, but that need not be the case. In general, you need a value→key function that returns an integer key for each element of your list, where “equivalent” elements return the same key. For a list of integers the value→key function is the identity function, so easy enough.For convenience, I will use lambdas for the value→key functions below.
Reducing Passes
You can reduce the number of times you bucket sort to only those bits that are not the same for all keys. Before the first pass through your list, you do not know anything about the keys, so we can gather information at the same time as the first bucket sort:
Once we have made that first pass, we can get a bitmask indicating which bits need sorting in our keys:
All remaining passes can now be managed with a simple loop, ending when we run out of bits needing sorting:
Bucket Sort
A linked list is perfect for bucket sorts of large, unwieldly objects. Remember: the bucket sort must be stable, meaning it must not mix up the order of “equivalent” elements. This is imperative for a radix sort to work properly.
For a binary bucket sort over a linked list, life is pretty simple — only two buckets are necessary, and you only need keep track of the last node in each bucket to append.
If you are using a doubly-linked list that bookkeeping is done for you. If you are using a singly-linked list you will have to do it manually.
Hopefully you can see how easy this would be to expand to any number of buckets. Still, we will stick with two.
Once you have split all the nodes into the two buckets, just join them back together into your new complete list. Don’t forget to clean up the tail’s
nextpointer!Be sure to check out trincot’s answer to see how he defined his singly-linked list with a
lastNodepointer and useful functions to make all this splitting into buckets (“partitions”) and joining the final list into invocations of very inexpensive member functions.Generics
This answer spends some time going on about keys and non-integer values. I have defined my list type’s nodes as:
And I have defined the sorting functions as:
Then, when I sorted my test lists, I used variations of:
You can do things like observe the stability in the sort by playing with the value→key function (lambda). For example, I could define a list of integers where the one’s digit didn’t matter:
Or a list of floats where the fractional part only mattered to two decimal places:
I could have a list of
Studentwhere I am only interested in sorting by the student’s ID:As long as the value→key function returns a sufficiently unique integer value we can use radix sort.