I was obsessed with optimizing the clutter for this algorithm, and below is what I finally came to. Lots of other code on the web and Stackoverflow is terribly bad. There are people trying to get the middle point of the list, doing recursion, having several loops for the remaining nodes, supporting calculations of a ton of things - ALL of this is not necessary. MergeSort is naturally suitable for a linked list, and the algorithm can be nice and compact, but it is not easy to get to that state.
Below the code supports the minimum number of variables and has the minimum number of logical steps necessary for the algorithm (i.e. without making the code unreachable / unreadable), as far as I know. However, I did not try to minimize LOC and kept as much free space as needed to maintain readability. I tested this code through fairly rigorous unit tests.
Please note that this answer combines several methods from another answer https://stackoverflow.com/a/464632/ Although the code is in C #, it should be trivial to convert to C ++, Java, etc.
SingleListNode<T> SortLinkedList<T>(SingleListNode<T> head) where T : IComparable<T> { int blockSize = 1, blockCount; do { //Maintain two lists pointing to two blocks, left and right SingleListNode<T> left = head, right = head, tail = null; head = null; //Start a new list blockCount = 0; //Walk through entire list in blocks of size blockCount while (left != null) { blockCount++; //Advance right to start of next block, measure size of left list while doing so int leftSize = 0, rightSize = blockSize; for (;leftSize < blockSize && right != null; ++leftSize) right = right.Next; //Merge two list until their individual ends bool leftEmpty = leftSize == 0, rightEmpty = rightSize == 0 || right == null; while (!leftEmpty || !rightEmpty) { SingleListNode<T> smaller; //Using <= instead of < gives us sort stability if (rightEmpty || (!leftEmpty && left.Value.CompareTo(right.Value) <= 0)) { smaller = left; left = left.Next; --leftSize; leftEmpty = leftSize == 0; } else { smaller = right; right = right.Next; --rightSize; rightEmpty = rightSize == 0 || right == null; } //Update new list if (tail != null) tail.Next = smaller; else head = smaller; tail = smaller; } //right now points to next block for left left = right; } //terminate new list, take care of case when input list is null if (tail != null) tail.Next = null; //Lg n iterations blockSize <<= 1; } while (blockCount > 1); return head; }
sights
- There is no special handling for cases such as list 0 from list 1, etc. These cases "just work."
- Many "standard" algorithms have two loops to pass through the remaining elements in order to handle the case when one list is shorter than the other. The above code eliminates the need for it.
- We are sure that the sorting is stable.
- The inner while loop, which is a hot spot, evaluates on average three expressions per iteration, which I think are minimal.
Update: @ ideasman42 has the C / C ++ code translated above , as well as suggestions for fixing comments and even greater improvement. The above code is now updated with this data.
Shital Shah Dec 27 '14 at 3:02 2014-12-27 03:02
source share