Talk:Merge sort

message: The article mentions a running time of O(n \log^2 n) for in-place merge sort, which is wrong. It should be O(n \log n).  — Preceding unsigned comment added by Dhruvbird (talk • contribs) 04:40, 16 March 2015 (UTC)
thread_title: In-place merge sort has the same time complexity as the out-of-place merge sort

message: Two sorted arrays can be merged in place in O(n) time, using O(1) space (temporary variable to swap two elements). To implement mergesort, this merge needs to be repeated log(n) times. You can easily implement mergesort bottom-up stably and in-place. Start with n=1, and merge the slices from i*n to i*(n+1) for i=0 until you exhaust the array. For n=1 this sorts pairs of elements. Then multiply n by 2 and restart until n >= size of the array.  — Preceding unsigned comment added by 87.198.115.102 (talk) 07:53, 26 June 2013 (UTC)
replies: text: It is a lot more difficult than you imagine, see  for instance. The problem is you start possibly having a number of strings to merge rather than just two or three after moving a few elements around. I don't think one can rightly just call it a mergesort after that but more of a qualified type of mergesort with a complicated algrithm. By the way new discussions should be put at the end, just click on 'new section' to start one up. Dmcq (talk) 08:54, 26 June 2013 (UTC)
thread_title: Mergesort does not require 2n space

message: This type of merge sort I read about first in 'Scheme and the Art of Programming', George Springer and Daniel P. Friedman. They call it a natural mergesort.
Here is a first stab (in python) at a bottom up , inplace merge sort:
def count_sorted( items, start=0 ):
  for x in range(start+1,len(items)):
    if items[x-1]>items[x]:
      return x - start ;
  return len(items) - start ;
def reinsert( items, val, start, end ):
  for x in range( start, end-1 ):
    if items[x+1] > val :
      items[x] = val
      return
    else:
      items[x] = items[x+1]
  items[end-1] = val
def merge_sublists( items, left, right ):
  for x in range(0,left):
    if items[x] > items[left]:
      val = items[x]
      items[x] = items[left]
      reinsert( items, val, left, left+right )
def merge( items ):
  left= count_sorted(items)
  while left < len(items):
    right = count_sorted( items, left )
    merge_sublists( items, left, right )
    left = left + right
A possible optimization is that when the longest ordered sublist is one, find the longest reverse ordered list and reverse it.
-David Medlock, Jan 13, 2006
:Is that timsort? --Damian Yerrick (talk | stalk) 17:45, 19 December 2011 (UTC)
thread_title:  Natural Mergesort 

message: It is claimed that the algorithm was invented by von Neumann in 1945. However the reference is to the Knuth book from 1998 (not the first edition, by the way!!!). The reference to an original work performed by von Neumann and published in 1945 is necessary. If the algorithm presented is described only in the Knuth book from 1998, it's from 1998, not from 1945!Riemann'sZeta (talk) 20:11, 12 April 2011 (UTC)
replies: text: A primary reference is not required. Knuth is WP:RS and the statement should be WP:V. If you are claiming that Knuth does not say von Neumann invented the algorithm in 1945, then that would be a WP:V attack. It does not sound like that is your dispute; instead you merely want a document with a 1945 date. That is not a WP requirement for a citation. Glrx (talk) 20:30, 12 April 2011 (UTC)
replies: text: It has been suggested that Von Neumann got the idea for merge sort from punched card collators which date back to 1937. The primary operation of a collator is to merge two sorted decks of punched cards, essentially one pass of a bottom up merge sort. collators. Rcgldr (talk) 16:18, 3 February 2020 (UTC)
thread_title:  The source of the algorithm in question 

message: As an algorithm that can be done recursively and non-recusively, it seems to me that added an example of a non-recursive merge sort algorithm might make this article more complete. I myself am not particularly apt at writing clear and understandable pseudocode, but I feel an example hear would be a good idea.OceanicMeerkat (talk) 05:52, 27 January 2014 (UTC)
replies: text: The example bottom up psuedo code near the top of the article is non-recursive. Rcgldr (talk) 08:19, 21 February 2014 (UTC)

text: The non-recursive merge sort is considerably faster than the recursive merge sort (40% faster). However it derives from tail end recursion removal and is quite tricky. The system I# for Java has an implementation of the non-recursive merge sort in the Java Language. The system I# for C# has the non-recursive merge sort in C#.  — Preceding unsigned comment added by 1.129.96.33 (talk) 08:01, 8 July 2016 (UTC)
thread_title:  Adding Non-Recursive pseudocode examples 

message: Just try to write that for the real computer. One of the obvious errors is that it splits [0, mid], [mid + 1, high] but then merges [0, mid - 1], [mid, high]  — Preceding unsigned comment added by 86.57.194.198 (talk) 06:28, 18 March 2014 (UTC)
replies: text: Did I fix it? Glrx (talk) 21:43, 26 March 2014 (UTC)
replies: text:  Assuming you added the comment about iEnd being the end of the array and not part of the array it's fixed. The example started off as actual working code, and most of the changes simply removed the variable type declarations. The split is [0, mid], and [mid, high], with the second index of each pair being the "end" of the sub-array and not part of the sub-array, so the actual split indices would be [0, mid-1], [mid, high-1]. Rcgldr (talk) 18:58, 29 April 2014 (UTC)
replies: text:  My edits. Glrx (talk) 23:43, 29 April 2014 (UTC)
thread_title: Top-down implementation is plain wrong

message: Anyone else think the animation example has extraneous comparisons once one of the sides (the left side in all cases) runs out of cells?
I thought the point of the whole algorithm is to take advantage of the fact that the partitions being merged are sorted.  — Preceding unsigned comment added by 88.192.22.239 (talk) 14:56, 14 April 2016 (UTC)
thread_title:  Animation example 

message: The article is completely obscure to those of us who can't read computer code. You break down the list into sub-lists, then you merge them - but how does that work? How do you merge two sorted lists into one? The article just assumes that's easy, as far as the text is concerned. The diagram also shows lists simply being merged and emerging as sorted, without explanation of the merge process. Cyclopaedic (talk) 09:07, 2 August 2016 (UTC)
replies: text: I strongly second that. The *one* thing this article is supposed to do it doesn't: Explaining the merge, as in merge sort. Nearly everything else in this whole long article isn't actually that special to merge sort. The merge step is.  — Preceding unsigned comment added by 2003:F3:EF19:EB00:5D7F:F4D5:2433:6888 (talk) 08:51, 9 April 2023 (UTC)
thread_title:  Could we have a description in English? 

message: I've reverted the midpoint formula back to the clearer (a+b)/2. The a+(b-a)/2 form is not obvious to most people.
WP should be describing algorithms. In a pseuodcode implementation, the widths of the numbers are not specified because the operations are abstract. The calculations are presumed to not overflow.
Also, the URL https://research.googleblog.com/2006/06/extra-extra-read-all-about-it-nearly.html is a blog. It is not a reliable source.
At Binary search algorithm#Implementation issues, a big point is made of the Java library bug, but that claim is disingenuous. There's a reason the bug was not discovered for years: it would only occur if somebody did something impractical. It would only be serious issue when one uses 32-bit indices in a 64-bit address space. But indices in 64-bit address spaces should be 64-bit numbers. In a 64-address space, I might want to make an array of more than 232 records; I should be able to use binary search on that array. If the system is only allowing 32-bit indices, then I have bigger problems.
Now the meat.
The numbers used in the algorithm are indices rather than byte pointers. In rational cases, the size of a record will be at least 4 bytes. So in the 32-bit address space case, the record indices will be 30-bit numbers. Overflow will not be an issue. Same for the 64-bit address space.
The Java bug manufactured an irrational case: it made an array of 1.1 billion single bytes and tried to search that array; the numbers overflowed and generated an array-out-of-bounds reference. Think about that array of bytes for a minute. A binary search of an array with 1.1 billion items that can only have 256 distinct items?
WP should not be emphasizing such didactic corner cases. Glrx (talk) 20:02, 10 October 2017 (UTC)
thread_title:  Expression for the midpoint 

message: This section includes the statements: "... producing an in-place merge algorithm that can be combined with a standard (top-down or bottom-up) merge sort ... " "the notion of "in-place" can be relaxed to mean "taking logarithmic stack space", because standard merge sort requires that amount of space for its own stack usage", but logarithmic stack space is only used by top down merge sort, not by bottom up merge sort. Rcgldr (talk) 16:43, 9 December 2017 (UTC)
replies: text: May be an in-place stable merge routine requires itself a logarithmic size of stack? Then combining it with a constant-stack-size bottom-up algorithm would produce a logaritmic-stack-size stable mergesort, wouldn't it? --CiaPan (talk) 17:49, 9 December 2017 (UTC)
replies: text:  My point is that the article states that "standard merge sort requires that amount of space", not standard merge sort modified to be in place. Rcgldr (talk) 00:06, 21 December 2017 (UTC)
thread_title:  Variants 

message: Copied from my talk page:
(I removed it in one other place; not sure why I didn't give as detailed an explanation here).  My issue is that it doesn't show the actual merging of sublists, which instead appears to happen instantaneously.  In contrast, the animation in the infobox does show the merging.  –Deacon Vorbis (carbon • videos) 12:22, 15 March 2019 (UTC)
replies: text: Of course the image does not show every detail of data transformation, it just shows a picture of the whole array at the end of each processing stage (here a processing stage being the merging routine). Similary :File:Bubble sort animation.gif or :File:Insertion sort animation.gif show an array state after each item reaches its destination place in the sorted part, but not after every swap. Additionally, the merging requires an external array, so the same presentation method simply can't show the whole process. Anyway it still is a presentation of mergesort. --CiaPan (talk) 13:01, 15 March 2019 (UTC)
replies: text: Well, I might argue that those aren't ideal either.  Slightly off-topic, but those have better alternatives available: :c:File:Sorting bubblesort anim.gif and :c:File:Insertion sort.gif.  I realize that a change in presentation would have to be made due to the use of an external array, but it could certainly be done.  Ehh, but until someone (possibly me, but let's be realistic) actually does that, if you really think it's better to include, then feel free to put it back; I won't continue to object.  –Deacon Vorbis (carbon • videos) 13:27, 15 March 2019 (UTC)

text: Not precisely the same GIF file, but the same animation has been there for over 12 years, since the end of November 2006 - see Special:Diff/90680635#Analysis. :) CiaPan (talk) 07:40, 20 March 2019 (UTC)
thread_title:  animation 

message: Consider that C, C++, C#, Java, Python, all handle array names as references, and can implement swap(A,B) which just swaps the references, so why not change the article to use swap(A,B), and put in a comment about using copy for programming languages that wouldn't support a swap. Rcgldr (talk) 18:22, 10 June 2019 (UTC)
replies: text: In C and descendants A and B are not 'references' to objects but rather names of variables. You can't just swap the meaning of the two to make them refer to opposite data. You'd have to replace pure arrays with pointers to arays and that would overwhelm the whole code with lots of unnecessary dereference operatos (prefix asterisks). As a result – IMHO – the code would be less readable, whilst readability should be one of the main aims of presenting the code in encyclopedia. Leave minor optimizations to programming handbooks. --CiaPan (talk) 06:10, 11 June 2019 (UTC)
replies: text:   Top down example already has minor optimization, it copies A[] to B[], then reverses the parameters on the recursive calls so that the direction merge changes with the level of recursion. Rcgldr (talk) 09:51, 15 June 2019 (UTC)

text: Additionally, you'd have to count loops and make another conditional swap at the end of the routine to make sure the sorted data eventually land in the correct array. Or return that array as an explicit result of the routine. --CiaPan (talk) 06:12, 11 June 2019 (UTC)
replies: text:  I meant when A[] and B[] are passed as parameters to a function. In C or C++, arrays passed as parameters are treated as pointers, myfun(int A[], int B[], ...) is the same as myfun(int *A, int *B, ...). Using sizeof(A) within myfun returns the size of a pointer, not the size of the array, so for C or C++, a 3rd parameter would be needed for the number of elements. Bottom up merge sort could use a counter for the number of passes and do a copy if the number of passes are odd, or similar to the top down example, which always copies at the start, bottom up could always copy at the end. Rcgldr (talk) 13:40, 11 June 2019 (UTC)
thread_title:  Bottom up implementation  - swapping reference to A and B 

message: Er... I just had a look at this talk page and noticed the extensive discussion of multiple versions of example code, after I just rewrote it.
As the commit message says, the motivation for the rewrite was TopDownSplitMerge(), which was:
// Sort the given run of array A[] using array B[] as a source.
// iBegin is inclusive; iEnd is exclusive (A[iEnd] is not in the set).
void TopDownSplitMerge(B[], iBegin, iEnd, A[])
{
    if(iEnd - iBegin < 2)                       // if run size == 1
        return;                                 //   consider it sorted
    // split the run longer than 1 item into halves
    iMiddle = (iEnd + iBegin) / 2;              // iMiddle = mid point
    // recursively sort both runs from array A[] into B[]
    TopDownSplitMerge(A, iBegin,  iMiddle, B);  // sort the left  run
    TopDownSplitMerge(A, iMiddle,    iEnd, B);  // sort the right run
    // merge the resulting runs from array B[] into A[]
    TopDownMerge(B, iBegin, iMiddle, iEnd, A);
}
This expects the recursive calls to TopDownSplitMerge to take their input from A[] and leave their results in B[], so the call to TopDownMerge() can copy them back into A[].  This implements an in-place sort.  But didn't we just say that it needs TopDownSplitMerge to be an out-of-place sort?
Upon even more reflection, it might be that the comment is seriously misleading and the code actually works in an incredibly confusing way because A[] and B[] are duplicates of each other so we recurse down to the bottom and find the source data in either A or B depending on the number of levels of recursion and then things work on the way back up.  But if I've stared at it for half an hour and still can't understand it, that's not a good pedagogical example.  (And q:Norm Schryer's wisdom applies.)
Almost as significantly, it uses a temporary buffer equal to the input size, which is a completely impractical implementation.    We want to avoid confusing micro-optimizations (like special-casing small arrays), but using a temporary buffer of realistic size has a pretty fundamental effect on how the code is organized.  You want to give an example whose structure is recognizably similar to a practical implementation.
The list code wasn't in such bad shape, but I didn't like how the top-down and bottom-up list code used different abstractions for the lists.  Such gratuitous differences make it difficult for a reader to see the significant differences.  One thing I specifically did was use the same core Merge operations in both examples.
I spent a while thinking about it and think I came up with pretty concise and clear implementations which don't leave out anything important (fiddly corner cases like zero-sized input are handled).  But given the amount of discussion which has taken place here, I probably should have proposed it here for discussion rather than just editing it into the article.  Oops.
One thing I'm not happy with and still needs fixing is the function names; the array and list sort functions currently have the same names.  Fortunately, that's a sufficiently minor point that it doesn't do significant harm to the article pending resolution.  I'm also not happy with the obscure bit-twiddling in the lsbit() function.  I put it in its own function and commented the hell out of it, but I can't find an existing article to link to.
Another problem which needs fixing is that the tape-based merging description refers to the bottom-up example code, but the example code (both before and after my rewrite) uses a depth-first merge order.  Tape merging operates breadth-first.  I at least added a few words about the difference (cut short before I digressed into cache-aware multi-way merges), but the tape merging reference needs fixed. Update: I fixed this.
Anyway, sorry for dropping a bomb into the article.  I'm used to editing quiet articles where the talk page is overrun with tumbleweeds and asking for comments there is a months-long process. so it's WP:BOLD or go home.  I should have checked this one first.  196.247.24.22 (talk) 00:50, 22 December 2019 (UTC)
replies: text: Okay, it's been a month with no comment of any sort.  I guess I didn't need to worry so much.  196.247.24.22 (talk) 22:42, 22 January 2020 (UTC)
replies: text:  The reason for no comments is probably due to the fact it's been about a year since the last change to the pseudocode and some of the members were not expecting any changes and not monitoring the article. I was away or busy with other projects and missed the changes that were made. Based on prior discussions on this, here and at other members talk pages, I reverted the examples back to their prior state. I'm not a fan of the mix between the top down and bottom up for linked list examples, but prior discussions wanted the top down left alone. Maybe now is the time to revisit this, but I'll have to somehow contact the prior members involved in this. I did add an explanation for how the top down for array merge sort works. Please sign up for a Wiki account so that you have a talk page providing a means for others to communicate with you. Rcgldr (talk) 16:27, 3 February 2020 (UTC)

text:  Your code (from Special:Permalink/931876827#Top-down implementation) seems incorrect. It looks like if fed with a two-items array  it will return  as a result. --CiaPan (talk) 10:12, 4 February 2020 (UTC)
replies: text:   For others reading this, Ciapan is referring to a prior edit by user:196.247.24.22 that was undone, and reverted back to a known working version: Special:Permalink/938979353#Top-down implementation Rcgldr (talk) 15:12, 4 February 2020 (UTC)
replies: text:  That's right, I am sorry for expressing myself in such ambiguous way. Thank you, , for explaining things. --CiaPan (talk) 15:51, 4 February 2020 (UTC)
thread_title:  I may have been a bit ''too'' [[WP:BOLD]] 

message: I just randomly stumbled upon the OR tag. I have to agree with MrOllie. You can't use your own unpublished research as a citation here. I'd cite the relevant wiki policy, but MrOllie did this already.  Stix1776 (talk) 10:59, 23 June 2022 (UTC)
thread_title: References

message: I am new to C++ and Wikipedia editing, (have been loosing sleep trying to find these bugs) so do not have the confidence to make this edit:-
I think there is an error in the TopDownMerge, the first if statement I think should not read
    if (i < iMiddle && (j >= iEnd || A[i] <= A[j])) {
but
    if (i <= iMiddle && (j > iEnd || A[i] <= A[j])) {
Reasoning:-
If the array has 2 elements i(0) will be equal to iMiddle(0) and j(1) will be equal to iEnd(1). On the first pass the first statement will now be true, the second will be false. The size of each element now determines which is copied into the other array first.
Rthepb (talk) 14:42, 25 November 2021 (UTC)
replies: text: That's wrong. When the array has two elements then iBegin equals 0, iMiddle equals 1 and iEnd equals 2. --CiaPan (talk) 21:50, 26 November 2021 (UTC)
thread_title:  possible error 

message: Worst case of time complexity for this algorithm is n*lg(n) according to Introduction to Algorithms. Not n*log(n) as noted in Analysis section.
--Siavoshkc (talk) 17:22, 24 September 2023 (UTC)
replies: text: Those are the same. Changing a logarithm base is equivalent to multiplying by some constant factor, and constant factors are meaningless in  of complexity. --CiaPan (talk) 19:06, 24 September 2023 (UTC)
replies: text: Thanks! It was bugging my mind for a while. Siavoshkc (talk) 08:11, 7 November 2023 (UTC)
thread_title:  Order of Complexity of Merge Sort 

message: Instead of scanning of lists to split them in half, a list size integer value is divided by 2 for each level of recursion, until a base case where size == 1, where a pointer | iterator to the next node is returned. The recursive merge sort updates (pass by reference) a pointer | iterator to the beginning node and returns a pointer | iterator to the ending node of a sorted list. Visual Studio 2022 switched to this method for std::list::sort. The following is a C++ example using iterators. Iterator names: li = left run iterator, ri = right run iterator = end left run iterator, ei = end right run iterator. Merge will splice (move and insert) sub-lists (multiple nodes) from right run, to left run for right run values < current left run value, else it just advances left run iterator. Splice requires a doubly linked list.
template <typename T>
typename std::list<T>::iterator Merge(std::list<T> &ll,
                             typename std::list<T>::iterator li,
                             typename std::list<T>::iterator ri,
                             typename std::list<T>::iterator ei)
{
    std::list<T>::iterator si;
    std::list<T>::iterator ni;
    ni = (*ri < *li) ? ri : li;
    while(1){
        if(*ri < *li){
            for(si = std::next(ri); si != ei && *si < *li; si++); 
            ll.splice(li, ll, ri, si);
            ri = si;
            if(ri == ei)
                return ni;
        } else {
            if(++li == ri)
                return ni;
        }
    }
}
template <typename T>
typename std::list<T>::iterator SortListR(std::list<T> &ll,
                                 typename std::list<T>::iterator &li,
                                          size_t size)
{
    if (size == 1)
        return std::next(li);
    std::list<T>::iterator ri;
    std::list<T>::iterator ei;
    ri = SortListR(ll, li, size-size/2);
    ei = SortListR(ll, ri,      size/2);
    li = Merge(ll, li, ri, ei);
    return ei;
}
template <typename T>
void SortList(std::list<T> &ll)
{
    if (ll.size() < 2)                  // return if nothing to do
        return;
    SortListR(ll, ll.begin(), ll.size());
}
Rcgldr (talk) 02:14, 24 April 2024 (UTC)
thread_title:  Recursive Merge Sort for Doubly Linked List - no list scanning to split sub-lists