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; ie, 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.
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
I have started learning python lately and the following is my attempt at implementing quicksort using python. Hope it is helpful. Feedback is welcomed :)
#!/usr/bin/python
Array = [ 3,7,2,8,1,6,8,9,6,9]
def partition(a, left, right):
pivot = left + (right - left)/2
a[left],a[pivot] = a[pivot], a[left] # swap
pivot = left
left += 1
while right >= left :
while left <= right and a[left] <= a[pivot] :
left += 1
while left <= right and a[right] > a[pivot] :
right -= 1
if left <= right:
a[left] , a[right] = a[right], a[left] # swap
left += 1
right -= 1
else:
break
a[pivot], a[right] = a[right] , a[pivot]
return right
def quicksort(array , left,right):
if left >= right:
return
if right - left == 1:
if array[right] < array[left]:
array[right], array[left] = array[left] , array[right]
return
pivot = partition(array, left, right)
quicksort(array, left, pivot -1)
quicksort(array, pivot+1,right)
def main():
quicksort(Array, 0 , len(Array) -1)
print Array
main()
Here is another implementation:
def quicksort(alist):
if len(alist) <= 1:
return alist
return part(alist,0,len(alist)-1)
def part(alist,start,end):
pivot = alist[end]
border = start
if start < end:
for i in range(start,end+1):
if alist[i] <= pivot:
alist[border], alist[i] = alist[i], alist[border]
if i != end:
border += 1
part(alist,start,border-1)
part(alist,border+1,end)
return alist
Here's what I came up with. The algorithm is in-place, looks nice and is recursive.
# `a` is the subarray we're working on
# `p` is the start point in the subarray we're working on
# `r` is the index of the last element of the subarray we're working on
def part(a,p,r):
k=a[r] #pivot
j,q=p,p
if p<r: # if the length of the subarray is greater than 0
for i in range(p,r+1):
if a[i]<=k:
t=a[q]
a[q]=a[j]
a[j]=t
if i!=r:
q+=1
j+=1
else:
j+=1
part(a,p,q-1) # sort the subarray to the left of the pivot
part(a,q+1,r) # sort the subarray to the right of the pivot
return a
def quicksort(a):
if len(a)>1:
return part(a,0,len(a)-1)
else:
return a
Explanation: Pivot is always the last element in given array. In my approach I keep track of the 'border' between numbers smaller and bigger than pivot. Border is an index of first number in 'bigger' group. At the end of each iteration we swap number under 'border' with the pivot number.
And the code:
def qs(ar, start, end):
if (end-start < 1):
return
if (end-start == 1):
if(ar[start] > ar[end]):
tmp = ar[start]
ar[start] = ar[end]
ar[end] = tmp
return
pivot = ar[end - 1]
border_index = start
i = start
while(i <= end - 1):
if (ar[i] < pivot):
if i > border_index:
tmp = ar[i]
ar[i] = ar[border_index]
ar[border_index] = tmp
border_index += 1
i+=1
ar[end-1] = ar[border_index]
ar[border_index] = pivot
qs(ar, start, border_index)
qs(ar, border_index + 1, end)
qs(ar, 0, n)
def quicksort(arr):
lesser = []
equal = []
greater = []
if len(arr)>1:
pivot = arr[len(arr)-1]
start = 0
while start<len(arr):
if arr[start] > pivot:
greater.append(arr.pop(start))
elif arr[start] < pivot:
lesser.append(arr.pop(start))
else:
start = start + 1
return (quicksort(lesser) + arr + quicksort(greater))
else:
return (arr)
print(quicksort([2,333,-22,0,54, 22, 37, 0.3, -2, 22]))
An in place implementation with pivot as the right most element
def partition(array, begin, end):
pivot = end
while begin < pivot:
if array[begin] > array[pivot]:
if begin != pivot - 1:
array[pivot], array[pivot - 1] = array[pivot - 1], array[pivot]
array[begin], array[pivot] = array[pivot], array[begin]
pivot-=1
else:
begin+=1
return pivot
def quicksort(array, begin=0, end=None):
if end is None:
end = len(array) - 1
if begin < end:
partition_index = partition(array, begin, end)
quicksort(array, begin, (partition_index - 1))
quicksort(array, (partition_index + 1), end)
Explanation - partition
1.Taking pivot as "end" - right most element
2.Bring pivot to its right position in sorted order.
3.Comparing element at begin with pivot element
Loop breaks when all elements are checked indicated by [begin] overlapping [pivot]. begin==pivot
If elem[begin] > elem[pivot]
The element is greater than elem[pivot]. It is at a wrong place. It has to come after pivot
Swap elements [pivot] and [pivot-1] if (pivot-1) != begin
Swap elements [pivot] and [begin]
Now the element at pivot is either in position [pivot-1] or at current [begin] position
Either way pivot has moved one place to the left. So decrement pivot by 1
Else
element at begin is <= to pivot and to the left of pivot.
So it is at an acceptable position
Increment begin to check for next element
Explanation - quick sort
1.Choosing a pivot, placing it in correct position in array and getting its index using the method "partition()".
2.Dividing array into left side and right side of pivot using begin, end and partition_index
3.Calling quicksort on left and right side array of pivot
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.