QuickSort is a widely used and efficient sorting algorithm that employs a divide-and-conquer strategy to sort elements. This guide covers the implementation of QuickSort in Go, along with optimizations and partitioning strategies to enhance its performance.
QuickSort works by selecting a 'pivot' element from the array and partitioning the other elements into two sub-arrays, according to whether they are less than or greater than the pivot. The sub-arrays are then sorted recursively.
Here is a straightforward implementation of QuickSort in Go:
gopackage main
import (
"fmt"
)
// QuickSort function
func QuickSort(arr []int) {
if len(arr) < 2 {
return
}
left, right := 0, len(arr)-1
// Choose the pivot
pivot := arr[len(arr)/2]
// Partitioning process
for left <= right {
for arr[left] < pivot {
left++
}
for arr[right] > pivot {
right--
}
if left <= right {
arr[left], arr[right] = arr[right], arr[left]
left++
right--
}
}
// Recursively apply to the sub-arrays
QuickSort(arr[:right+1])
QuickSort(arr[left:])
}
func main() {
arr := []int{10, 7, 8, 9, 1, 5}
QuickSort(arr)
fmt.Println("Sorted array:", arr)
}
The choice of pivot significantly affects QuickSort's performance. A poor pivot choice can degrade QuickSort to O(n^2) complexity. Here are some strategies for choosing a better pivot:
gofunc medianOfThree(arr []int, low, high int) int {
mid := (low + high) / 2
if arr[low] > arr[mid] {
arr[low], arr[mid] = arr[mid], arr[low]
}
if arr[low] > arr[high] {
arr[low], arr[high] = arr[high], arr[low]
}
if arr[mid] > arr[high] {
arr[mid], arr[high] = arr[high], arr[mid]
}
return mid
}
func partition(arr []int, low, high int) int {
pivotIndex := medianOfThree(arr, low, high)
pivot := arr[pivotIndex]
arr[pivotIndex], arr[high] = arr[high], arr[pivotIndex]
i := low
for j := low; j < high; j++ {
if arr[j] < pivot {
arr[i], arr[j] = arr[j], arr[i]
i++
}
}
arr[i], arr[high] = arr[high], arr[i]
return i
}
Tail call optimization reduces the depth of recursive calls. For QuickSort, it involves sorting the smaller partition first and using a loop to handle the larger partition.
gofunc QuickSort(arr []int, low, high int) {
for low < high {
pivotIndex := partition(arr, low, high)
if pivotIndex-low < high-pivotIndex {
QuickSort(arr, low, pivotIndex-1)
low = pivotIndex + 1
} else {
QuickSort(arr, pivotIndex+1, high)
high = pivotIndex - 1
}
}
}
Combining the above optimizations, here is a more efficient implementation of QuickSort in Go:
gopackage main
import (
"fmt"
"math/rand"
"time"
)
func medianOfThree(arr []int, low, high int) int {
mid := (low + high) / 2
if arr[low] > arr[mid] {
arr[low], arr[mid] = arr[mid], arr[low]
}
if arr[low] > arr[high] {
arr[low], arr[high] = arr[high], arr[low]
}
if arr[mid] > arr[high] {
arr[mid], arr[high] = arr[high], arr[mid]
}
return mid
}
func partition(arr []int, low, high int) int {
pivotIndex := medianOfThree(arr, low, high)
pivot := arr[pivotIndex]
arr[pivotIndex], arr[high] = arr[high], arr[pivotIndex]
i := low
for j := low; j < high; j++ {
if arr[j] < pivot {
arr[i], arr[j] = arr[j], arr[i]
i++
}
}
arr[i], arr[high] = arr[high], arr[i]
return i
}
func QuickSort(arr []int, low, high int) {
for low < high {
pivotIndex := partition(arr, low, high)
if pivotIndex-low < high-pivotIndex {
QuickSort(arr, low, pivotIndex-1)
low = pivotIndex + 1
} else {
QuickSort(arr, pivotIndex+1, high)
high = pivotIndex - 1
}
}
}
func main() {
rand.Seed(time.Now().UnixNano())
arr := []int{10, 7, 8, 9, 1, 5}
QuickSort(arr, 0, len(arr)-1)
fmt.Println("Sorted array:", arr)
}
QuickSort is a powerful and efficient sorting algorithm when implemented with proper optimizations. By choosing an appropriate pivot strategy and applying tail call optimization, you can significantly improve QuickSort's performance. Understanding these techniques is crucial for writing efficient and robust sorting algorithms in Go.