使用 Go 语言实现排序算法及其性能基准测试
排序算法是计算机科学中最基本且最重要的算法之一。本文将介绍如何使用 Go 语言实现三种常见的排序算法:插入排序、快速排序和堆排序,并通过基准测试(Benchmark)来比较它们的性能。
1. 插入排序
插入排序是一种简单的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上通常使用 in-place 排序(即只需用到 O(1) 的额外空间的排序)。
实现
package sort
import "fmt"
// InsertionSort 插入排序
func InsertionSort(arr []int) {
for i := 1; i < len(arr); i++ {
key := arr[i]
j := i - 1
for j >= 0 && arr[j] > key {
arr[j+1] = arr[j]
j--
}
arr[j+1] = key
}
}
func main() {
arr := []int{12, 11, 13, 5, 6}
fmt.Println("Original array:", arr)
InsertionSort(arr)
fmt.Println("Sorted array:", arr)
}
2. 快速排序
快速排序是一种高效的排序算法,采用分治法的策略。它的基本思想是通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,然后分别对这两部分记录继续进行排序,以达到整个序列有序。
实现
package sort
import "fmt"
// QuickSort 快速排序
func QuickSort(arr []int) {
quickSortHelper(arr, 0, len(arr)-1)
}
func quickSortHelper(arr []int, low, high int) {
if low < high {
pi := partition(arr, low, high)
quickSortHelper(arr, low, pi-1)
quickSortHelper(arr, pi+1, high)
}
}
func partition(arr []int, low, high int) int {
pivot := arr[high]
i := low - 1
for j := low; j < high; j++ {
if arr[j] < pivot {
i++
arr[i], arr[j] = arr[j], arr[i]
}
}
arr[i+1], arr[high] = arr[high], arr[i+1]
return i + 1
}
func main() {
arr := []int{10, 7, 8, 9, 1, 5}
fmt.Println("Original array:", arr)
QuickSort(arr)
fmt.Println("Sorted array:", arr)
}
3. 堆排序
堆排序是一种基于二叉堆的数据结构的排序算法。堆是一个近似完全二叉树的结构,并同时满足堆属性:即子节点的键值或索引总是小于(或大于)它的父节点。
实现
package sort
import "fmt"
// HeapSort 堆排序
func HeapSort(arr []int) {
n := len(arr)
// Build heap (rearrange array)
for i := n/2 - 1; i >= 0; i-- {
heapify(arr, n, i)
}
// One by one extract an element from heap
for i := n - 1; i > 0; i-- {
arr[0], arr[i] = arr[i], arr[0] // Move current root to end
heapify(arr, i, 0) // call max heapify on the reduced heap
}
}
func heapify(arr []int, n int, i int) {
largest := i // Initialize largest as root
left := 2*i + 1 // left = 2*i + 1
right := 2*i + 2 // right = 2*i + 2
// If left child is larger than root
if left < n && arr[left] > arr[largest] {
largest = left
}
// If right child is larger than largest so far
if right < n && arr[right] > arr[largest] {
largest = right
}
// Change root, if needed
if largest != i {
arr[i], arr[largest] = arr[largest], arr[i] // swap
// Recursively heapify the affected sub-tree
heapify(arr, n, largest)
}
}
func main() {
arr := []int{12, 11, 13, 5, 6, 7}
fmt.Println("Original array:", arr)
HeapSort(arr)
fmt.Println("Sorted array:", arr)
}
4. 基准测试
为了比较不同排序算法的性能,我们可以使用 Go 语言的 testing
包来进行基准测试。基准测试可以帮助我们了解每种算法在不同数据规模下的表现。
基准测试代码
package sort
import (
"math/rand"
"testing"
"time"
)
func BenchmarkInsertionSort(b *testing.B) {
for i := 0; i < b.N; i++ {
arr := generateRandomArray(1000)
InsertionSort(arr)
}
}
func BenchmarkQuickSort(b *testing.B) {
for i := 0; i < b.N; i++ {
arr := generateRandomArray(1000)
QuickSort(arr)
}
}
func BenchmarkHeapSort(b *testing.B) {
for i := 0; i < b.N; i++ {
arr := generateRandomArray(1000)
HeapSort(arr)
}
}
func generateRandomArray(size int) []int {
rand.Seed(time.Now().UnixNano())
arr := make([]int, size)
for i := range arr {
arr[i] = rand.Intn(10000)
}
return arr
}
运行基准测试
在终端中运行以下命令来执行基准测试:
go test -bench .
基准测试结果
基准测试的结果可能会因系统配置和随机数据的不同而有所差异。以下是一个示例输出:
使用代码:
//sort_do_test.go
package sort
import (
"math/rand"
"testing"
"time"
)
func BenchmarkInsertionSort(b *testing.B) {
for i := 0; i < b.N; i++ {
arr := generateRandomArray(1000)
InsertionSort(arr)
}
}
func BenchmarkQuickSort(b *testing.B) {
for i := 0; i < b.N; i++ {
arr := generateRandomArray(1000)
QuickSort(arr)
}
}
func BenchmarkHeapSort(b *testing.B) {
for i := 0; i < b.N; i++ {
arr := generateRandomArray(1000)
HeapSort(arr)
}
}
func generateRandomArray(size int) []int {
rand.Seed(time.Now().UnixNano())
arr := make([]int, size)
for i := range arr {
arr[i] = rand.Intn(10000)
}
return arr
}
package sort
//sort_do.go
// InsertionSort 插入排序
func InsertionSort(arr []int) {
for i := 1; i < len(arr); i++ {
key := arr[i]
j := i - 1
for j >= 0 && arr[j] > key {
arr[j+1] = arr[j]
j--
}
arr[j+1] = key
}
}
// QuickSort 快速排序
func QuickSort(arr []int) {
quickSortHelper(arr, 0, len(arr)-1)
}
func quickSortHelper(arr []int, low, high int) {
if low < high {
pi := partition(arr, low, high)
quickSortHelper(arr, low, pi-1)
quickSortHelper(arr, pi+1, high)
}
}
func partition(arr []int, low, high int) int {
pivot := arr[high]
i := low - 1
for j := low; j < high; j++ {
if arr[j] < pivot {
i++
arr[i], arr[j] = arr[j], arr[i]
}
}
arr[i+1], arr[high] = arr[high], arr[i+1]
return i + 1
}
// HeapSort 堆排序
func HeapSort(arr []int) {
n := len(arr)
// Build heap (rearrange array)
for i := n/2 - 1; i >= 0; i-- {
heapify(arr, n, i)
}
// One by one extract an element from heap
for i := n - 1; i > 0; i-- {
arr[0], arr[i] = arr[i], arr[0] // Move current root to end
heapify(arr, i, 0) // call max heapify on the reduced heap
}
}
func heapify(arr []int, n int, i int) {
largest := i // Initialize largest as root
left := 2*i + 1 // left = 2*i + 1
right := 2*i + 2 // right = 2*i + 2
// If left child is larger than root
if left < n && arr[left] > arr[largest] {
largest = left
}
// If right child is larger than largest so far
if right < n && arr[right] > arr[largest] {
largest = right
}
// Change root, if needed
if largest != i {
arr[i], arr[largest] = arr[largest], arr[i] // swap
// Recursively heapify the affected sub-tree
heapify(arr, n, largest)
}
}