6

here is a merge sort logic in python : (this is the first part, ignore the function merge()) The point in question is converting the recursive logic to a while loop. Code courtesy: Rosettacode Merge Sort

def merge_sort(m):
    if len(m) <= 1:
        return m

    middle = len(m) / 2
    left = m[:middle]
    right = m[middle:]

    left = merge_sort(left)
    right = merge_sort(right)
    return list(merge(left, right))

Is it possible to make it a sort of dynamically in the while loop while each left and right array breaks into two, a sort of pointer keeps increasing based on the number of left and right arrays and breaking them until only single length sized list remains? because every time the next split comes while going on both left- and right- side the array keeps breaking down till only single length list remains, so the number of left sided (left-left,left-right) and right sided (right-left,right-right) breaks will increase till it reaches a list of size 1 for all.

Dan
  • 4,312
  • 16
  • 28
user2290820
  • 2,709
  • 5
  • 34
  • 62

4 Answers4

4

One possible implementation might be this:

def merge_sort(m):
    l = [[x] for x in m]                  # split each element to its own list
    while len(l) > 1:                     # while there's merging to be done 
        for x in range(len(l) >> 1):      # take the first len/2 lists
            l[x] = merge(l[x], l.pop())   # and merge with the last len/2 lists
    return l[0] if len(l) else []

Stack frames in the recursive version are used to store progressively smaller lists that need to be merged. You correctly identified that at the bottom of the stack, there's a one-element list for each element in whatever you're sorting. So, by starting from a series of one-element lists, we can iteratively build up larger, merged lists until we have a single, sorted list.

Dan
  • 4,312
  • 16
  • 28
  • ive one doubt here. merge(l[x], l.pop()); l.pop() pops out the last list element and merge compares the l[x] list element with the last one.shouldnt it be merge(l[x],l[x+1])? – user2290820 Nov 11 '13 at 11:30
  • You need to be reducing the size of `l` with each pass, hence the `pop`. If you wanted to be sure you always merged adjacent sublists, you could do `merge(l[x], l.pop(x+1))`, but the algorithm works either way. It shouldn't matter what merges with what so long as the resulting new sublist is sorted. – Dan Nov 11 '13 at 11:33
  • so there is no performance slow down ? I mean merge via comparing two extreme values and finally stacks join again and merge() function has to do a lot comparing Vs lesser comparing while doing merge(l[x], l.pop(x+1)) ; i hope you got the point – user2290820 Nov 11 '13 at 11:35
  • The number of compares mergesort does is _O(n_ lg _n)_ either way. If anything my version is faster because `pop` from the end of a list is special cased in [the `listpop` function in the python source code](http://svn.python.org/view/python/trunk/Objects/listobject.c?view=markup#l927). – Dan Nov 11 '13 at 11:42
  • 1
    merging *adjacent* entries gives you a *stable* sort variant though. – Will Ness Nov 11 '13 at 11:44
  • 1
    @WillNess can you explain what do you mean by stable sort variant by doing merging of adjacent entries? – user2290820 Nov 11 '13 at 12:21
  • 1
    calling `l.pop(x+1)` one by one will be slow. To make it stable, better to use `for x in range(len(l) >> 1): l[x] = merge(l[2*x], l[2*x+1])` and then, after the `for` loop, remove the `l`'s second half slice at once (possibly keeping the last element, if len(l) was odd), `l[(len(l)>>1):(len(l)>>1)*2]=[]`. – Will Ness Nov 13 '13 at 22:22
2

Reposted from alternative to recursion based merge sort logic at the request of a reader:

One way to eliminate recursion is to use a queue to manage the outstanding work. For example, using the built-in collections.deque:

from collections import deque
from heapq import merge

def merge_sorted(iterable):
    """Return a list consisting of the sorted elements of 'iterable'."""
    queue = deque([i] for i in iterable)
    if not queue:
        return []
    while len(queue) > 1:
        queue.append(list(merge(queue.popleft(), queue.popleft())))
    return queue[0]
Community
  • 1
  • 1
  • 1
    +1. though this too is a non-stable variant of mergesort (when the length of the input list is anything but a power of 2). the case where the coding approach is *nice*, but ... -- And if we change it to produce new list, twice shorter, on each step, then it is not a queue anymore; more importantly, then the problem appears of dangling non-merged short stubs near the end of the list. Same problem (of dangling short stubs) exists also for [this](http://stackoverflow.com/questions/19903985/alternative-to-recursion-based-merge-sort-logic/19907169#comment29719469_19905146) modification. – Will Ness Nov 18 '13 at 09:35
  • 1
    (contd.) to deal with that problem (like e.g. for *2^n+1* initial length), we must alternate the direction at which we sweep the intermediate list of lists, at each step. – Will Ness Nov 18 '13 at 09:41
  • 1
    another thing is, we don't actually need to create the list of lists; we can just work on the original array, and one other copy of it, in the alternating fashion, going forth and back by 2x sized chunks each time (which we'll treat as *already ordered*, by previous steps). – Will Ness Nov 18 '13 at 09:49
1

It's said, that every recursive function can be written in a non-recursive manner, so the short answer is: yes, it's possible. The only solution I can think of is to use the stack-based approach. When recursive function invokes itself, it puts some context (its arguments and return address) on the inner stack, which isn't available for you. Basically, what you need to do in order to eliminate recursion is to write your own stack and every time when you would make a recursive call, put the arguments onto this stack.

For more information you can read this article, or refer to the section named 'Eliminating Recursion' in Robert Lafore's "Data Structures and Algorithms in Java" (although all the examples in this book are given in Java, it's pretty easy to grasp the main idea).

Community
  • 1
  • 1
aga
  • 27,954
  • 13
  • 86
  • 121
0

Going with Dan's solution above and taking the advice on pop, still I tried eliminating while and other not so pythonic approach. Here is a solution that I have suggested: PS: l = len

My doubt on Dans solution is what if L.pop() and L[x] are same and a conflict is created, as in the case of an odd range after iterating over half of the length of L?

def merge_sort(m):
    L = [[x] for x in m]  # split each element to its own list
    for x in xrange(l(L)):      
        if x > 0:
            L[x] = merge(L[x-1], L[x])
    return L[-1]

This can go on for all academic discussions but I got my answer to an alternative to recursive method.

user2290820
  • 2,709
  • 5
  • 34
  • 62
  • 1
    it is never the same; `a >> 1` is like `a div 2` so for odd numbers 1 is left as remainder, i.e. the middle element is not processed - not touched. -- what gives mergsort its efficiency is the (roughly balanced) *tree* of merges; you introduce great disbalance, your tree is degenerate - making it actualy an insertion sort, an O(n^2) algorithm (your calls to merge always have a singleton list as its 2nd argment). – Will Ness Nov 11 '13 at 16:01
  • @WillNess any other non recursive implementation,if you could offer – user2290820 Nov 16 '13 at 12:48
  • there was another answer here with good code in it, using queues; I've contacted a moderator to see if and how this contents can be brought back. It'll take a day or so, probably. In the mean time, see if you can tweak the code into using a queue (pop 2 from the front, append merged result at the back). This will also be unstable; still it's an interesting variation to it. Try `from collections import deque`, `from heapq import merge`, ... – Will Ness Nov 16 '13 at 17:25
  • @WillNess will ive tried deque and merge from heapq. the rosetta code is a python try for implementing merge(check the link in the question); I don't know why that answer was taken down but yes thats a better hack. – user2290820 Nov 17 '13 at 13:51