package main import ( "fmt" "testing" ) // QuickSortHoare 使用 Hoare 双指针分区:l 主动向右找 >= pivot,r 主动向左找 <= pivot, // 两指针相遇时分区完成。取中间元素作 pivot 避免已排序/逆序退化; // 内层用严格不等号,相等元素也让两指针停下交换,防止全相等数组死循环。 func QuickSortHoare(nums []int) { var helper func(start, end int) helper = func(start, end int) { if start >= end { return } pivot := nums[start+(end-start)/2] // 取中间元素,避免已排序/逆序退化 l, r := start, end for { for nums[l] < pivot { // l 主动向右找 >= pivot 的 l++ } for nums[r] > pivot { // r 主动向左找 <= pivot 的 r-- } if l >= r { // 两指针相遇/交叉,分区完成 break } nums[l], nums[r] = nums[r], nums[l] l++ r-- // 各进一步,相等元素也会交换,防止全相等数组死循环 } helper(start, r) helper(r+1, end) } helper(0, len(nums)-1) } // QuickSortLomuto 使用 Lomuto 单向分区:pivot 放 nums[end],i 维护"<= pivot 区"的右边界+1, // j 从左扫到 end-1,遇到 <= pivot 的就与 nums[i] 交换并 i++。 // 循环结束后 swap(nums[i], nums[end]) 让 pivot 归位到 i,左半 <= pivot、右半 > pivot。 func QuickSortLomuto(nums []int) { var helper func(start, end int) helper = func(start, end int) { if start >= end { return } pivot := nums[end] // 取最后一个作 pivot i := start // i 指向"<= pivot 区"的右边界+1 for j := start; j < end; j++ { if nums[j] <= pivot { nums[i], nums[j] = nums[j], nums[i] i++ } } nums[i], nums[end] = nums[end], nums[i] // 把 pivot 放到最终位置 i helper(start, i-1) helper(i+1, end) } helper(0, len(nums)-1) } func TestQuickSort(t *testing.T) { nums := []int{1, 2, 1, 2, 1, 2, 3, 1, 3, 2} QuickSortHoare(nums) fmt.Printf("Hoare: %+v\n", nums) nums2 := []int{1, 2, 1, 2, 1, 2, 3, 1, 3, 2} QuickSortLomuto(nums2) fmt.Printf("Lomuto: %+v\n", nums2) } func BubbleSort(nums []int) { for i := 0; i < len(nums); i++ { swapped := false for j := 0; j < len(nums)-1-i; j++ { if nums[j] > nums[j+1] { nums[j], nums[j+1] = nums[j+1], nums[j] swapped = true } } if !swapped { break } } } func TestBubbleSort(t *testing.T) { nums := []int{1, 2, 1, 2, 1, 2, 3, 1, 3, 2} BubbleSort(nums) fmt.Printf("TestBubbleSort: %+v\n", nums) } // SelectionSort 选择排序:每轮在 [i, n) 找最小值,与 nums[i] 交换。 // 交换次数少(每轮最多一次),但比较次数固定 O(n²),不稳定。 func SelectionSort(nums []int) { for i := 0; i < len(nums); i++ { minIdx := i for j := i; j < len(nums); j++ { if nums[j] < nums[minIdx] { minIdx = j } } nums[i], nums[minIdx] = nums[minIdx], nums[i] } } // InsertionSort 插入排序:把 nums[i] 往前插到 [0, i) 的正确位置。 // 近乎有序时接近 O(n);是很多高级排序在小数组时的收尾段。稳定。 func InsertionSort(nums []int) { // for i := 1; i < len(nums); i++ { // for j := i; j > 0; j-- { // if nums[j] < nums[j-1] { // nums[j], nums[j-1] = nums[j-1], nums[j] // } // } // } for i := 1; i < len(nums); i++ { key := nums[i] j := i for j > 0 && nums[j-1] > key { nums[j] = nums[j-1] // 往右挪一格 j-- } nums[j] = key // 落到正确位置 } } // MergeSort 归并排序:分治——左半排好 + 右半排好,再合并。 // 稳定,最坏 O(n log n),需 O(n) 额外空间。合并用双指针。 func MergeSort(nums []int) { tmp := make([]int, len(nums)) // 全局临时数组,只分配一次 var helper func(start, end int) helper = func(start, end int) { if start >= end { return } mid := start + (end-start)/2 helper(start, mid) helper(mid+1, end) // merge [start,mid] 和 [mid+1,end] → tmp → 拷回 nums p1, p2, k := start, mid+1, start for p1 <= mid && p2 <= end { if nums[p1] <= nums[p2] { tmp[k] = nums[p1] p1++ } else { tmp[k] = nums[p2] p2++ } k++ } for p1 <= mid { tmp[k] = nums[p1] p1++ k++ } for p2 <= end { tmp[k] = nums[p2] p2++ k++ } copy(nums[start:end+1], tmp[start:end+1]) // 关键:拷回原切片 } helper(0, len(nums)-1) } // HeapSort 堆排序:建大顶堆,反复把堆顶(最大值)与末尾交换并下沉调整。 // 原地、最坏 O(n log n),不稳定。top-K / 优先队列的基础。 func HeapSort(nums []int) { n := len(nums) if n <= 1 { return } // siftDown:在 [0, end] 范围内把 nums[i] 下沉到正确位置 siftDown := func(i, end int) { for 2*i+1 <= end { l, r := 2*i+1, 2*i+2 largest := i if l <= end && nums[l] > nums[largest] { largest = l } if r <= end && nums[r] > nums[largest] { largest = r } if largest == i { break } nums[i], nums[largest] = nums[largest], nums[i] i = largest } } // 阶段 1:建大顶堆,从最后一个父节点往前 for i := n/2 - 1; i >= 0; i-- { siftDown(i, n-1) } // 阶段 2:反复把堆顶(最大)与末尾交换,堆范围 -1,修复堆 for end := n - 1; end > 0; end-- { nums[0], nums[end] = nums[end], nums[0] siftDown(0, end-1) // 注意:堆范围是 [0, end-1],排除已归位的 nums[end] } } // ShellSort 希尔排序:按递减步长分组做插入排序,最后步长为 1 退化为插入排序。 // 平均约 O(n^1.3),非稳定。代码短,比插入排序在乱序数据上快很多。 func ShellSort(nums []int) { gap := len(nums) for gap > 1 { gap /= 2 for i := 0; i < len(nums); i++ { // 插入排序 for j := 1; j*gap+i < len(nums); j++ { for k := j; k > 0; k-- { if nums[k*gap+i] < nums[(k-1)*gap+i] { nums[k*gap+i], nums[(k-1)*gap+i] = nums[(k-1)*gap+i], nums[k*gap+i] } else { break } } } } } } // ShellSortStandard 是更常见的希尔排序写法:不显式遍历每一个分组, // 而是让 j 每次减 gap,使其天然只在 nums[i%gap] 这一组内移动。 // 相比 ShellSort 的交换式插入,这里先保存 key、再右移元素,能减少写入次数。 func ShellSortStandard(nums []int) { for gap := len(nums) / 2; gap > 0; gap /= 2 { for i := gap; i < len(nums); i++ { key := nums[i] j := i for j-gap >= 0 && key < nums[j-gap] { nums[j] = nums[j-gap] j -= gap } nums[j] = key } } } // CountingSort 计数排序:元素为 [0, k] 整数,统计频次后前缀和定位。 // O(n+k),k=值域大小。非比较,稳定版本需倒序填充。 func CountingSort(nums []int) { // TODO: 你来写(假设元素均 >= 0) } // BucketSort 桶排序:按值把元素分到 n 个桶,每桶用 InsertionSort 排好,再依次写回原数组。 // 平均 O(n+k),k=桶数;最坏 O(n²)(全挤进一个桶,退化成插入排序)。 // 稳定(InsertionSort 稳定)。需 min/max 决定桶映射区间。 func BucketSort(nums []int) { n := len(nums) if n <= 1 { return } // 找值域 minVal, maxVal := nums[0], nums[0] for _, v := range nums { if v < minVal { minVal = v } if v > maxVal { maxVal = v } } if minVal == maxVal { return // 全相等,无需排 } // 桶数取 n;idx = (v-minVal) * n / (maxVal-minVal+1),分母 +1 保证 maxVal 不越界 buckets := make([][]int, n) for _, v := range nums { idx := (v - minVal) * n / (maxVal - minVal + 1) buckets[idx] = append(buckets[idx], v) } // 每桶内部插入排序(稳定),再依次拷回原数组 k := 0 for _, b := range buckets { InsertionSort(b) copy(nums[k:], b) k += len(b) } } func TestSortAlgos(t *testing.T) { cases := [][]int{ {1, 2, 1, 2, 1, 2, 3, 1, 3, 2}, {3, 2, 1}, {5, 4, 3, 2, 1}, {2, 1}, {1, 3, 2, 3, 1}, {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, {1}, {}, {2, 2, 2, 2}, {3, 3, 3, 1, 1, 1, 2, 2, 2}, } sorters := []struct { name string fn func([]int) }{ // {"SelectionSort", SelectionSort}, // {"InsertionSort", InsertionSort}, // {"MergeSort", MergeSort}, // {"HeapSort", HeapSort}, // {"BucketSort", BucketSort}, // {"ShellSort", ShellSort}, {"ShellSortStandard", ShellSortStandard}, // {"CountingSort", CountingSort}, } for _, s := range sorters { for _, c := range cases { got := append([]int(nil), c...) s.fn(got) fmt.Printf("%s in=%v got=%v\n", s.name, c, got) } fmt.Println() } }