Merge sort implementation in python 3

Posted on

Problem

I have implemented a Merge sort in Python 3, and it works well. If anything needs to be improved, I would appreciate the criticism.

def merge_sort(nums):

    if len(nums) == 1:                                  
        return

    middle_index = len(nums) // 2

    left_half = nums[:middle_index]
    right_half = nums[middle_index:]

    merge_sort(left_half)
    merge_sort(right_half)

    i = 0
    j = 0
    k = 0

    while i<len(left_half) and j<len(right_half):
        if left_half[i] < right_half[j]:
            nums[k] = left_half[i]
            i = i + 1
        else:
            nums[k] = right_half[j]
            j = j + 1

        k = k + 1

    while i<len(left_half):
        nums[k] = left_half[i]
        k = k + 1
        i = i + 1       

if __name__ == "__main__":

   nums = [-3,-2,-1,1,2,1,0,-1,-2,-3]
   merge_sort(nums)
   print(nums)

Solution

Everything I’m going to talk about is in the merge_sort function

General

i = 0
j = 0
k = 0

Can be defined as

i = j = k = 0


You should always leave spaces between operators as per PEP 8 rules

i<len(left_half) should be i < len(left_half)


Use x += y instead of x = x + y


In my opinion, I think using short and concise names such as mid or middle instead of middle_index would be better. If you don’t wish to, you can leave it as it!


Use type hints


Add docstrings


Bug

Your function only takes into account the left_half of the array, and ignores what’s left in the right_half

For example, if nums array was [3, 9, 0], The array would be [0, 3, 0]

This would happen as

merge_sort([3]) which won’t change the left_half
merge_sort([9, 0]) which would make the right_half as [0, 9]

Then,

left_half = [3]
right_half = [0, 9]

nums = [3, 9, 0]

i = 0
j = 0
k = 0

First, the else statement would be called as 3 > 0.

i = 0
j = 1
k = 1

nums = [0, 9, 0]

Next, the if statement would be called as 3 < 9

i = 1
j = 1
k = 2

nums = [0, 3, 0]

Now, the while loop will terminate as i = len(left_side)

Then, while i < len(left_side) would immediately terminate as i = len(left_side)

Did you notice? right_side still has one element 9 waiting to be traversed, but it never will be.

To fix that, add the following to the end of the function

while j < len(right_half):
    nums[k] = right_half[j]
    j += 1
    k += 1

Improvement

Now, instead of using a while loop at all, you can just use a[k:] = left_half[i:] + right_half[j:] to replace both the loops! This is true because one half must be empty and the other half must have the length of n - k.


Performance

If you are using this function in real time with an array of a really large size, this won’t work efficiently.

len takes quite a bit of time. To make it even faster, use a parameter length which would be the length of the array


The final implementation of the function:

from typing import List, Any

def merge_sort(nums: List[Any], length: int) -> None:
    """ Uses Merge Sort to sort an array """

    # Base case
    if length == 1:
        return

    mid = length // 2

    left, right = mid, length - mid

    left_half, right_half = nums[:mid], nums[mid:]

    merge_sort(left_half, left)
    merge_sort(right_half, right)

    i = j = k = 0

    while i < left and j < right:
        if left_half[i] < right_half[j]:
            nums[k] = left_half[i]
            i += 1
        else:
            nums[k] = right_half[j]
            j += 1

        k += 1

    nums[k:] = left_half[i:] + right_half[j:]

Note: Any in typing means any datatype is allowed. The function can sort any datatype that is comparable with another element of the same datatype.

For one you could change your code to non-recursive. Lists in python and recursion don’t mix well. In other words, what you did might work fine, but it could work a bit better.

Leave a Reply

Your email address will not be published. Required fields are marked *