Reputation: 443
I am trying to implement a quick sort algorithm in Ruby. I got a simple one working pretty quickly but it involved creating temporary arrays on the fly. I'm trying to implement a more streamlined quick sort that only swaps elements in place without the need to create additional arrays.
Does anyone know why this code doesn't work? I've been following the pattern laid out here in exact detail but I can't get it to work properly.
def quicksort(arr = [], left = 0, right = 0)
right = arr.length - 1 if right < 1
if left < right
index = partition(arr, left, right)
quicksort(arr, left, index - 1)
quicksort(arr, index, right)
end
arr
end
def partition(arr, left, right)
pivot = arr[(left + right) / 2]
while left <= right
left += 1 while arr[left] < pivot
right -= 1 while arr[right] > pivot
if left <= right
arr[left], arr[right] = arr[right], arr[left]
left += 1
right -= 1
end
end
left
end
arr = [8, 10, 13, 5, 6, 20, 2, 43, 9, 11, 15]
p quicksort(arr)
Upvotes: 1
Views: 170
Reputation: 11
If you want to print every step of quick sort you can do like this:
def advanced_quicksort(arr, low = 0, high = arr.length - 1)
if low < high
pivot_index = partition(arr, low, high)
puts "[#{arr.join(', ')}]" #you can change it to ```puts arr.join(' ') else
advanced_quicksort(arr, low, pivot_index - 1)
advanced_quicksort(arr, pivot_index + 1, high)
end
end
def partition(arr, low, high)
pivot = arr[high]
i = low - 1
(low..high - 1).each do |j|
if arr[j] < pivot
i += 1
arr[i], arr[j] = arr[j], arr[i]
end
end
arr[i + 1], arr[high] = arr[high], arr[i + 1]
i + 1
end
# Example usage:
arr = [1, 3, 9, 8, 2, 7, 5]
puts "[#{arr.join(', ')}]"
advanced_quicksort(arr)
Upvotes: 0
Reputation: 28826
If sorting large arrays, the stack space can be limited to O(log(n)), but worst case time complexity will remain at O(n^2). I don't know ruby syntax, but the ideal is to only recurse on the smaller partition, and loop back for the larger partition:
def quicksort(arr, left = 0, right = arr.length - 1)
while left < right
index = partition(arr, left, right)
if((index - left) < (right-index))
quicksort(arr, left, index - 1)
left = index
else
quicksort(arr, index, right)
right = index-1
end
arr
end
Upvotes: 0
Reputation: 164819
If we throw a debugging p "Left #{left}, Right #{right}
...
def quicksort(arr = [], left = 0, right = 0)
right = arr.length - 1 if right < 1
if left < right
index = partition(arr, left, right)
quicksort(arr, left, index - 1)
...
We find there's a problem. left
is never set. It's always the default of 0. And right is doing its own thing.
"Left 0, Right 10"
"Left 0, Right 8"
"Left 0, Right 2"
"Left 0, Right 1"
"Left 0, Right 10"
"Left 0, Right 7"
"Left 0, Right 6"
"Left 0, Right 5"
"Left 0, Right 2"
"Left 0, Right 1"
"Left 0, Right 10"
"Left 0, Right 4"
"Left 0, Right 2"
"Left 0, Right 1"
"Left 0, Right 10"
"Left 0, Right 5"
"Left 0, Right 2"
"Left 0, Right 1"
"Left 0, Right 10"
"Left 0, Right 5"
"Left 0, Right 2"
"Left 0, Right 1"
The problem is right = arr.length - 1 if right < 1
. If right
is ever < 1 it's set back to the end of the array. left
is always 0 so left
is always less than right
. quicksort(arr, 0, index - 1)
is recursed into over and over again. quicksort(arr, index, right)
is never reached.
Your partition
is fine, and good eye noticing pivot
can be calculated inside pivot
.
What tripped you up is those defaults. You're setting a default for right
any time it's less than 1. But it should only be set if it isn't passed in at all.
def quicksort(arr, left = 0, right = arr.length - 1)
if left < right
index = partition(arr, left, right)
quicksort(arr, left, index - 1)
quicksort(arr, index, right)
end
arr
end
Now quicksort(array)
is equivalent to quicksort(array, 0, array.length - 1)
. Subsequent recursive calls pass left
and right
in, so no need for defaults.
And no default for the array. If they forget to pass an array in that should be an ArgumentError.
I prefer the public wrapper approach used in the video. If someone accidentally passes in too many arguments they get a clear ArgumentError rather than something weird happening.
# Using the ! convention for functions which alter their arguments.
def quicksort!(array)
_quicksort!(array, 0, array.length - 1)
end
private def _quicksort!(array, left, right)
...
end
Upvotes: 2