Quick Sort Python Program - python

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).

Related

Quick Sort Algorithm using python

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

Using binary search to find the last position of an element

I'm trying to write a function that returns the last position of an element in a sorted list, however I'm not quite sure of how binary search works. Can someone help me fix the problem in my code?
def last_entry(L, target):
low = 0
high = len(L) - 1
while low <= high:
mid = (low + high) // 2
if L[mid] < target:
low = mid + 1
elif L[mid] > target:
high = mid - 1
else:
if mid == len(L) - 1:
return mid
if target < L[mid + 1]:
return mid
return -1
The problem lies here:
else:
if mid == len(L) - 1:
return mid
if target < L[mid + 1]:
return mid
It's incomplete. If you use the input list [0, 0, 1, 3, 3, 3, 4, 8] and target 3, it will loop infinitely (I haven't tested it though). So you need to keep searching in the upper half for the target:
else:
if mid == len(L) - 1:
return mid
if target < L[mid + 1]:
return mid
low = mid + 1
Edit: I tested my solution and I think it works
You can separate the problems. First we do the binary search, then we verify if it's the latest occurrence in list.
lst = [1, 2, 2, 2, 3, 4, 4, 5, 6, 7, 7, 7, 8, 8, 9]
def search(lst, target):
min = 0
max = len(lst)-1
avg = (min+max)//2
while (min < max):
if (lst[avg] == target):
return avg
elif (lst[avg] < target):
return avg + 1 + search(lst[avg+1:], target)
else:
return search(lst[:avg], target)
return avg
def find_latest(lst, index):
current = lst[index]
index += 1
while lst[index] == current:
current = lst[index]
index += 1
return index - 1
find_latest(lst ,search(lst, 4))

My bisection functions have a small error

My recursive and iterative bisection functions wont work with long lists of the same number.
def bisection_it(mylist, goal):
start, end = 0, (len(mylist) - 1)
while start <= end:
mid = (start + end) // 2
if goal == mylist[mid]:
return mid
if goal < mylist[mid]:
end = mid - 1
else:
start = mid + 1
return -1
def bisection_rec(mylist, goal, start = 0, end = None):
if end is None:
end = len(mylist) - 1
if start > end:
return -1
mid = (start + end) // 2
if goal == mylist[mid]:
return mid
if goal < mylist[mid]:
return bisection_rec(mylist, goal, start = 0, end = mid-1)
return bisection_rec(mylist, goal, mid+1, end)
with the list [2, 2, 2, 2, 2, 2, 2, 2, 2,] I expect the output to be 0, but the actual output is 4

pythonic quicksort. call stack exceeded

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)

quick sort implementation using python, can't figure out where I'm wrong in the implementation

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]

Categories

Resources