1

I'm working on understanding and implementing a merge sort. I'm running into a brick wall on this one, and can't seem to get an implementation that works. My current implementation hits a "list index out of range" error. Here is my code:

def merge_sort(list_a):
    mid = len(list_a) // 2
    print('Mid is ', mid)
    while len(list_a) > 1:
        left = list_a[:mid]
        print('Left is now ', left)
        right = list_a[mid:]
        print('Right is now ', right)
        merge_sort(left)
        merge_sort(right)
        merge(list_a, left, right)


def merge(comb_list, list_a, list_b):
    print('Starting the merge.')
    a1, b1, c1 = 0, 0, 0
    na, nb, nc = len(list_a), len(list_b), len(comb_list)
    while a1 < na and b1 < nb:
        if list_a[a1] < list_b[b1]:
            print('Adding from A')
            comb_list[c1] = list_a[a1]
            a1 += 1
        else:
            print('Adding from B')
            comb_list[c1] = list_b[b1] 
            b1 += 1

        c1 += 1

    while list_a:
        comb_list[c1] = list_a[a1]
        c1 += 1
        a1 += 1

    while list_b:
        comb_list[c1] = list_b[b1]
        c1 += 1
        b1 += 1

if __name__ == '__main__':
    list_a = [54,26,93,17,77,31,44,55,20]
    merge_sort(list_a)
2
  • 2
    while list_a: means while len(list_a) != 0:. You don't modify your list_a inside while block, so this cycle is interminable, variable a1 grows and go out of list length. Commented Dec 9, 2015 at 11:53
  • en.wikipedia.org/wiki/Merge_sort Commented Dec 9, 2015 at 12:21

2 Answers 2

1

I have made three changes to your script to get it to work. As pointed by sshdup while list_a will always evaluate to true as you don't remove any elements inside the loop. Therefore I have changed while list_a: to len(list_a)>a1, while list_b: to len(list_b)>b1. I also added return merge(list_a, left, right) to your merge_sort method in line with the pseudo code. After adding the return statement the while in merge_sort can also be replaced with an if statement. I have tested this on a random array of integers and it seems to work, however, as usual you should test your edge cases to make sure it works as expected.

def merge_sort(list_a):
    mid = len(list_a) // 2
    print('Mid is ', mid)
    if len(list_a) > 1:
        left = list_a[:mid]
        print('Left is now ', left)
        right = list_a[mid:]
        print('Right is now ', right)
        merge_sort(left)
        merge_sort(right)
        return merge(list_a, left, right)


def merge(comb_list, list_a, list_b):
    print('Starting the merge.')
    a1, b1, c1 = 0, 0, 0
    na, nb, nc = len(list_a), len(list_b), len(comb_list)
    while a1 < na and b1 < nb:
        if list_a[a1] < list_b[b1]:
            print('Adding from A')
            comb_list[c1] = list_a[a1]
            a1 += 1
        else:
            print('Adding from B')
            comb_list[c1] = list_b[b1] 
            b1 += 1

        c1 += 1

    while len(list_a)>a1:
        comb_list[c1] = list_a[a1]
        del list_a[a1]
        c1 += 1
        a1 += 1

    while len(list_b)>b1:
        comb_list[c1] = list_b[b1]        
        c1 += 1
        b1 += 1

if __name__ == '__main__':
    list_a = [54,26,93,17,77,31,44,55,20]
    merge_sort(list_a)
    print list_a
Sign up to request clarification or add additional context in comments.

Comments

1

To make the code work, you would have to make 2 adjustments:

  1. Replace the while loop in the 4th line with an if-statement
  2. Change the code of the while loops in the merge() function a bit

The working code:

def merge_sort(list_a):
    mid = len(list_a) // 2
    print('Mid is ', mid)
    #Use if statement instead
    if len(list_a) > 1:
        left = list_a[:mid]
        print('Left is now ', left)
        right = list_a[mid:]
        print('Right is now ', right)
        merge_sort(left)
        merge_sort(right)
        merge(list_a, left, right)
        #Print the result
        print(list_a)
        #Or return it directly:
        #return list_a


def merge(comb_list, list_a, list_b):
    print('Starting the merge.')
    a1, b1, c1 = 0, 0, 0
    na, nb, nc = len(list_a), len(list_b), len(comb_list)
    while a1 < na and b1 < nb:
        if list_a[a1] < list_b[b1]:
            print('Adding from A')
            comb_list[c1] = list_a[a1]
            a1 += 1
        else:
            print('Adding from B')
            comb_list[c1] = list_b[b1] 
            b1 += 1

        c1 += 1
    #Change while loop:
    while a1 < na:
        comb_list[c1] = list_a[a1]
        c1 += 1
        a1 += 1
    #Change while loop:
    while b1 < nb:
        comb_list[c1] = list_b[b1]
        c1 += 1
        b1 += 1

if __name__ == '__main__':
    list_a = [54,26,93,17,77,31,44,55,20]
    merge_sort(list_a)

You might want to return the result directly, to do that just add

return list_a

at the end of your merge_sort() function. With that approach, you could print the result directly using print(merge_sort(list_a)) in the main method.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.