简体   繁体   中英

Difficulty with merge sort algorithm

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)

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM