Search code examples
pythonalgorithmsortingmergesort

Getting List Index out of Range Error while using Merge Sort function


I am getting List index out of range error. I have also used GeeksforGeeks program as a reference but still got that error. I get no error when I run this without using it inside Merge_Sort() function.

def Merge(arr, p, q, r):
    n1 = q-p+1
    n2 = r-q
    L = [0]*n1
    M = [0]*n2

    for i in range(0, n1):
        L[i] = arr[p+i-1]
    for j in range(0, n2):
        M[j] = arr[q+j]
    i = 0
    j = 0
    for k in range(r-1):
        if L[i] <= M[j]:
            arr[k] = L[i]
            i = i+1
        else:
            arr[k] = M[j]
        j = j+1

def Merge_Sort(arr, p, r):
    if p < r:
        q = int((p+r)/2)
        Merge_Sort(arr, p, q)
        Merge_Sort(arr, q+1, r)
        Merge(arr, p, q, r)

ar = [5, 3, 6, 1, 2, 9, 7, 8]
n = len(ar)
Merge_Sort(ar, 1, n)
print(ar)
Error:
 line 14, in Merge
    if L[i]<=M[j]:

IndexError: list index out of range

Solution

  • The code is incorrect: the is some confusion about index values and slice boundaries, and other mistakes too:

    • array indices start at 0 in python, so you should call Merge_sort(ar, 0, n)

    • the slice length n1 is off by one, it should be n1 = q - p

    • the test for recursion should compute the slice length and only recurse for slices with at least 2 elements.

    • the merge loop is incorrect: you should test if i and j are both inside the slices. An efficient way to do this is to replace the comparison with this test:

      if i < n1 and (j == n2 or L[i] <= M[j]):
      
    • the merge loop should iterate for k from p to r excluded, not from 0 to r excluded

    • the increment code for j is misaligned, it should be indented one more step

    It is much simpler to consider the first index to be included and the second to be excluded. There are far too many tutorials in the net in various languages that insist on other conventions, invariably causing confusion for newbie programmers.

    Here is a corrected and simplified version:

    def Merge(arr, p, q, r):
        n1 = q - p
        n2 = r - q
        L = arr[p : q]
        M = arr[q : r]
        i = 0
        j = 0
        for k in range(p, r):
            if i < n1 and (j == n2 or L[i] <= M[j]):
                arr[k] = L[i]
                i = i + 1
            else:
                arr[k] = M[j]
                j = j + 1
    
    def Merge_Sort(arr, p, r):
        if r - p >= 2:
            q = (p + r) // 2
            Merge_Sort(arr, p, q)
            Merge_Sort(arr, q, r)
            Merge(arr, p, q, r)
    
    ar = [5, 3, 6, 1, 2, 9, 7, 8]
    Merge_Sort(ar, 0, len(ar))
    print(ar)
    

    Note that you can further simplify the MergeSort function with a single temporary array if you ensure that the left slice is always at least as large as the right slice:

    def Merge(arr, p, q, r):
        tmp = arr[p : q]
        i = 0
        n = q - p
        while i < n:
            if q == r or tmp[i] <= arr[q]:
                arr[p] = tmp[i]
                i += 1
                p += 1
            else:
                arr[p] = arr[q]
                q += 1
                p += 1
    
    def Merge_Sort(arr, p, r):
        if r - p >= 2:
            q = (p + r + 1) // 2
            Merge_Sort(arr, p, q)
            Merge_Sort(arr, q, r)
            Merge(arr, p, q, r)
    
    ar = [5, 3, 6, 1, 2, 9, 7, 8]
    Merge_Sort(ar, 0, len(ar))
    print(ar)