Hi there! Today’s challenge is another algorithm that I learned more in detail
in the Algorithms: Design and Analysis, Part 1 course from Coursera:
QuickSort. This algorithm chooses a pivot and does a partial sort around that
pivot, having all elements less than the pivot to its left and all greater to
its right, considering we want the number in increasing order. The main function
is qsort
which calls the function to choose a pivot and another to partition
around the pivot.
The way I learned in class considered the pivot to be the first element of the
array and then I had indexes i
and j
to separate the portions of the array.
All elements to the left of position i
were smaller than the pivot, excluding
the pivot itself in position 0. This left all elements greater than the pivot to
the right of position i
. Something I took some time to understand was that
this is actually generic. I can choose any pivot as long as I swap it with the
first element.
There were three ways of choosing a pivot that I considered when doing the
assignment, but here I present just the last one, namely the median of three
method. I consider the elements in the first, middle and last positions in the
array and then I take the median of these three elements to be the pivot. The
getPivot
function returns the index of the pivot so that the qsort
function
can use it. In the end of a qsort
call, the element is sorted in place and the
number of comparisons is returned. For each recursive call of an array of size
m
, I count m-1
comparisons.
The solution is presented below. Notice I expect the number of elements n
in
the array as the first input and then n
elements separated by \n
.
package main
import (
"fmt"
)
// returns the index of the median here
func argmedian(index, values []int) int {
swaps := 1
// simple bubble sort to sort three values
// and then we return the middle value
for swaps > 0 {
swaps = 0
for i := 0; i < len(index)-1; i++ {
if values[i+1] < values[i] {
swap(values, i, i+1)
swap(index, i, i+1)
swaps++
}
}
}
return index[1]
}
func getPivot(a []int) int {
index := make([]int, 3)
values := make([]int, 3)
index[0] = 0
values[0] = a[0]
if len(a)%2 == 0 {
index[1] = len(a)/2 - 1
values[1] = a[len(a)/2-1]
} else {
index[1] = len(a) / 2
values[1] = a[len(a)/2]
}
index[2] = len(a) - 1
values[2] = a[len(a)-1]
return argmedian(index, values)
}
func swap(a []int, i, j int) {
a[i], a[j] = a[j], a[i]
}
func partition(a []int, pivot_i int) (int, int) {
swap(a, pivot_i, 0)
p := a[0] // pivot
finalPlace := 0
if len(a) > 2 {
i := 0
pivotNotInPlace := false
for j := 1; j < len(a); j++ {
if a[j] < p {
i++
swap(a, i, j)
pivotNotInPlace = true
}
}
// pivot in rightful position
// case of len(a) > 2
if pivotNotInPlace {
a[0] = a[i]
a[i] = p
finalPlace = i
}
} else if len(a) == 2 {
if a[1] < p {
a[0] = a[1]
a[1] = p
finalPlace = 1
}
}
return finalPlace, len(a) - 1
}
func qsort(a []int) int {
n := 0 // number of comparisons
if len(a) <= 1 {
return n
}
pivot_index := getPivot(a)
i, n0 := partition(a, pivot_index)
n += n0
n += qsort(a[:i])
n += qsort(a[i+1:])
return n
}
func main() {
// Input file has N
// in the first line
N := 0
fmt.Scanf("%d\n", &N)
// allocate memory
X := make([]int, N)
for i := 0; i < N; i++ {
fmt.Scanf("%d\n", &X[i])
}
// n is the number of comparisons
// quicksort has done
n := qsort(X)
for i := 0; i < N; i++ {
fmt.Printf("%d\n", X[i])
}
fmt.Printf("#Comparisons: %d\n", n)
}
That’s all for this day of coding in Go. Thanks for readings!