Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In-place QuickSort in Python

I had to implement the QuickSort algorithm for a homework in a language of my choice and I chose Python.

During the lectures, we've been told that QuickSort is memory efficient because it works in-place; i.e., it has no additional copies of parts of the input array for recursions.

With this in mind, I tried to implement the QuickSort algorithm in Python, but shortly afterwards realized that in order to write an elegant piece of code I would have to pass parts of the array to the function itself while recursing. Since Python creates new lists each time I do this, I have tried using Python3 (because it supports the nonlocal keyword). The following is my commented code.

def quicksort2(array):
# Create a local copy of array.
arr = array

    def sort(start, end):
        # Base case condition
        if not start < end:
            return

        # Make it known to the inner function that we will work on arr
        # from the outer definition
        nonlocal arr

        i = start + 1
        j = start + 1

        # Choosing the pivot as the first element of the working part
        # part of arr
        pivot = arr[start]

        # Start partitioning
        while j <= end:
            if arr[j] < pivot:
                temp = arr[i]
                arr[i] = arr[j]
                arr[j] = temp
                i += 1
            j += 1
        temp = arr[start]
        arr[start] = arr[i - 1]
        arr[i - 1] = temp
        # End partitioning

        # Finally recurse on both partitions
        sort(start + 0, i - 2)
        sort(i, end)
    sort(0, len(array) - 1)

Now, I'm not sure whether I did the job well or am I missing something. I have written a more Pythonic version of QuickSort but that surely doesn't work in-place because it keeps returning parts of the input array and concatenates them.

My question is, is this the way of doing it in Python? I have searched both Google and SO but haven't found a true in-place implementation of QuickSort, so I thought it'd be best to ask.

like image 246
Can Ibanoglu Avatar asked Jul 21 '13 14:07

Can Ibanoglu


1 Answers

Sure not the best way, plus this famous algorithm will have dozens of perfect implementations.. this is mine, quite easy to understand

def sub_partition(array, start, end, idx_pivot):

    'returns the position where the pivot winds up'

    if not (start <= idx_pivot <= end):
        raise ValueError('idx pivot must be between start and end')

    array[start], array[idx_pivot] = array[idx_pivot], array[start]
    pivot = array[start]
    i = start + 1
    j = start + 1

    while j <= end:
        if array[j] <= pivot:
            array[j], array[i] = array[i], array[j]
            i += 1
        j += 1

    array[start], array[i - 1] = array[i - 1], array[start]
    return i - 1

def quicksort(array, start=0, end=None):

    if end is None:
        end = len(array) - 1

    if end - start < 1:
        return

    idx_pivot = random.randint(start, end)
    i = sub_partition(array, start, end, idx_pivot)
    #print array, i, idx_pivot
    quicksort(array, start, i - 1)
    quicksort(array, i + 1, end)

Ok first a seperate function for the partition subroutine. It takes the array, the start and end point of interest, and the index of pivot. This functions should be clear

Quicksort then call the partition subroutine for the first time on the whole array; then call recursevely itself to sort everything up to the pivot and everything after.

ask if you dont understand something

like image 103
Ant Avatar answered Oct 12 '22 01:10

Ant