I took the first element as starting and pivot value.
Incrementing start position when pivot is less than end value.
Decrementing end position when pivot is greater than end value.
Also, swapping starting and ending position when each pass completes.
If starting position cross the end position then I'm swapping pivot and end elements.
def partition(arr, lb, ub):
pivot = arr[lb]
start = lb
print('start', start)
end = ub
print('end', end)
while start <= end:
while arr[start] <= pivot:
start += 1
while arr[end] >= pivot:
end -= 1
if start <= end:
arr[start], arr[end] = arr[end], arr[start]
else:
arr[end], arr[lb] = arr[lb], arr[end]
return end
def quickSort(arr, lb, ub):
if lb >= ub:
return 0
loc = partition(arr, lb, ub)
quickSort(arr, lb, loc-1)
quickSort(arr, loc+1, ub)
arr = [10, 4, 7, 3, 8, 6, 9, 1, 5, 2]
n = len(arr)
print(n)
quickSort(arr, 0, len(arr) -1)
print ("Sorted array is:")
for i in range(n):
print("%d" % arr[i])
I am getting the following error:
IndexError
---> quickSort(arr, 0, len(arr) -1)
Error in partition(arr, lb, ub)
---> while arr[start] <= pivot:
IndexError: list index out of range
Can someone please tell whats wrong in this code?
This is the direct cause of the error:
while arr[start] <= pivot:
start += 1
while arr[end] >= pivot:
end -= 1
You don't check whether start isn't larger than ub (and whether end isn't smaller than lb). This is what you should do:
while start < ub and arr[start] <= pivot:
start += 1
while end > lb and arr[end] >= pivot:
end -= 1
However, there's another thing that is wrong:
while start <= end:
should be
while start < end:
because for the weak inequality the loop never ends - it reaches the state of start == end and loops there endlessly. The inequality here:
if start <= end:
also should be a strong one, otherwise for start == end you're swapping arr[end] with arr[start] (ie. itself) instead of arr[lb] (ie. the pivot).
And finally, formatting of your code posted here was botched, so I took the liberty of fixing it.
Not sure if you still don't have a solution yet, but here is a revised partition function.
def partition(arr, lb, ub):
pivot = arr[lb]
start = lb + 1
print('start', start)
end = ub
print('end', end)
while start <= end:
while start <= end and arr[start] <= pivot:
start += 1
while end >= start and arr[end] > pivot:
end -= 1
if start < end:
arr[start], arr[end] = arr[end], arr[start]
if start >= end:
start -= 1
arr[start], arr[lb] = arr[lb], arr[start]
return start
You need to check whether start does not increments more than the end such as following,
while arr[start] <= pivot:
if start<end:
start += 1
Related
I'm trying to figure out why my program isn't fully sorting the arr, here's the code below, output: [7, 9, 6, 1, 5, 10, 15, 7, 2]. Maybe my partition function is wrong.
def quickSort(arr, lower_bound, upper_bound):
if lower_bound >= upper_bound:
return
if lower_bound < upper_bound:
loc = partition(arr, lower_bound, upper_bound)
quickSort(arr, lower_bound, loc-1)
quickSort(arr, loc+1, upper_bound)
def partition(arr, lower_bound, upper_bound):
pivot = arr[lower_bound]
start = lower_bound
end = upper_bound
while start < end:
while start <= end and arr[start] <= pivot:
start += 1
while start <= end and arr[end] >= pivot:
end -= 1
if start < end:
arr[start], arr[end] = arr[end], arr[start]
else:
break
arr[start], arr[end] = arr[end], arr[start]
return end
arr = [7, 6, 10, 5, 9, 2, 1, 15, 7]
quickSort(arr, 0, len(arr)-1)
print(arr)
The error is in the line after the while loop:
arr[start], arr[end] = arr[end], arr[start]
When execution arrives at this statement, start and end are equal, so this accomplishes nothing. After the while loop ends, the pivot value -- which is still sitting at lower_bound -- should be moved to where start and end have met. So correct to:
arr[lower_bound], arr[end] = arr[end], pivot
And now return end is doing what it should do: return the index where the pivot element is sitting, separating the not-greater values (left) from the not-smaller values (right).
Heres my QuickSort Algorithm that I came up with. The first function is the Partitioning, and the second function is the QuickSorting of an array.
def Partition(A ,start, end):
pivot = A[end]
p_index = start
for i in range(start,end-1):
if A[i] <= pivot:
#swap
A[i], A[i-1] = A[p_index], A[i]
p_index = p_index +1
# this is swaping the pivot point between the sorted values
A[p_index], A[end] = A[end], A[p_index]
return p_index
def QuickSort(A, start, end):
if start < end:
return
p_index = Partition(A, start, end)
# recursive Calls here:
QuickSort(A, start, p_index-1)
QuickSort(A,p_index +1, end)
The original array is returned unsorted
array = [2,1,3,6,8,5,7,4]
n = len(array)
QuickSort(array,0,n-1)
print(array)
returned: [2,1,3,6,8,5,7,4]
Your base case logic is incorrect.
You want to return when 'start' is greater or equal to 'end':
def QuickSort(A, start, end):
if start >= end:
return
p_index = Partition(A, start, end)
# recursive Calls here:
QuickSort(A, start, p_index-1)
QuickSort(A,p_index +1, end)
This is my attempted implementation of quicksort in python
However, I notice that I have a max call stack exceeded error, likely because the length of my collection never goes down.
When printing, I notice that my recursive call does not update the "end" variable
def swap(collection, index1, index2):
temp = collection[index1]
collection[index1] = collection[index2]
collection[index2] = temp
def partition(collection, idx1, idx2):
left = idx1
right = idx2
pivot = len(collection)//2
while(left < right):
while(collection[left] < collection[pivot]):
left += 1
while(collection[right] > collection[pivot]):
right -= 1
if left < right:
swap(collection, left, right)
left +=1
right -=1
return left
def quickSort(collection, start, end):
#Check for base case
if len(collection) > 1:
index = partition(collection, start, end)
newIdx = index - 1
if start < newIdx:
quickSort(collection, start, newIdx)
if index < right:
quickSort(collection, index, end)
return collection
array = [7,5, 10, 12, 3,2,9]
quickSort(array, 0, len(array) - 1)
print(array)
Below is the code that I'm implementing for quick sort in python. But I'm not able to get where I'm going wrong with the implementation. Any leads?
def quicksort(arr, size):
partition(arr, size)
def partition(arr, size):
if size <= 1:
return
left = 0
right = size - 1
pivot = arr[size/2]
while left < right:
while arr[left] < pivot:
left += 1
while arr[right] > pivot:
right -= 1
temp = arr[left]
arr[left] = arr[right]
arr[right] = temp
partition(arr, left)
partition(arr[left:], len(arr[left:]))
arr = [1,2,3,4,5,45,3,5,4,6]
quicksort(arr, len(arr))
You are sorting new slices:
partition(arr[left:], len(arr[left:]))
Those slices are new lists, not the original list object. Anything you do to those won't be visible in the original value of arr.
If you want to do the sorting in-place, you'll need to pass along start and stop values of what section of the array to sort, not pass along slices:
def quicksort(arr):
partition(arr, 0, len(arr) - 1)
def partition(arr, start, stop):
if stop - start < 1:
return
left = start
right = stop
pivot = arr[start + ((stop - start) / 2)]
while left <= right:
while arr[left] < pivot:
left += 1
while arr[right] > pivot:
right -= 1
if left <= right:
arr[left], arr[right] = arr[right], arr[left]
left += 1
right -= 1
partition(arr, start, right)
partition(arr, left, stop)
I've made a few more tweaks, adjusting the boundary tests and using tuple assignment to swap the two elements.
This version sorts correctly:
>>> def quicksort(arr):
... partition(arr, 0, len(arr) - 1)
...
>>> def partition(arr, start, stop):
... if stop - start < 1:
... return
... left = start
... right = stop
... pivot = arr[start + ((stop - start) / 2)]
... while left <= right:
... while arr[left] < pivot:
... left += 1
... while arr[right] > pivot:
... right -= 1
... if left <= right:
... arr[left], arr[right] = arr[right], arr[left]
... left += 1
... right -= 1
... partition(arr, start, right)
... partition(arr, left, stop)
...
>>> arr = [1,2,3,4,5,45,3,5,4,6]
>>> quicksort(arr)
>>> arr
[1, 2, 3, 3, 4, 4, 5, 5, 6, 45]
The problem is to find the index of the element which is less than or equal to N. To tackle the problem, I wrote the following code but it seems to be not working.
def find_index(primes, N, start, end):
mid = int((start + end)/2)
if start == end:
return start
if primes[mid - 1] < N:
if primes[mid] == N:
return mid
elif primes[mid] > N:
return mid - 1
else:
return find_index(primes, N, start, mid + 1)
elif primes[mid - 1] > N:
if primes[mid] > N:
return find_index(primes, N, mid - 1, end)
What obvious condition am I missing? Is there any better method to find the index in O(log(n))?
If you have a list of size 2 or larger, divide and conquer:
def find_index(primes, N, start, end):
mid = int((start + end)/2)
if start == end:
return start
if end - start == 1:
return end if primes[end] < N else start
if primes[mid] == N:
return mid
elif primes[mid] < N:
return find_index(primes, N, mid, end)
else:
return find_index(primes, N, start, mid)
The logic here being that choosing sublists via midpoint will eventually yield a difference between start and end of 1. This can be assumed for the following reasons:
Since (start + end)/2 = floor((start + end)/2), the midpoint will eventually be 1 index away from the upper boundary.
A list of size 2 (e.g. start/end 3/4 or 2/3) will always yield a list of size 2 since the midpoint will be the lower bound. This is evident given (n + n + 1)/2 = (2n + 1)/2 => 2n/2 = n.
Once a difference of 1 has been reached, the top and bottom can be checked for satisfying the requirement of being less than N.
Completely empty list case not handled.