面试必考、算法基础必掌握,一篇吃透所有经典排序
排序算法是计算机科学最基础、最重要的知识点之一,不管是面试还是工程开发,都会高频出现。这篇文章带你一次性掌握 10 大经典排序算法,包含:原理讲解 + Go 语言完整可运行代码 + 时间 / 空间复杂度 + 稳定性总结收藏这一篇就够了!
10 大排序算法总览(速查表)
| 排序算法 | 时间复杂度 | 空间复杂度 | 特点 / 备注 |
|---|---|---|---|
| 冒泡排序 | O(n²) | O(1) | 交换排序,最简单,最好情况 O (n) |
| 选择排序 | O(n²) | O(1) | 不稳定,交换次数少 |
| 插入排序 | O(n²) | O(1) | 近乎有序数据效率极高,最好 O (n) |
| 希尔排序 | O(n¹·³) | O(1) | 插入排序升级版,分组插入,不稳定 |
| 归并排序 | O(n log n) | O(n) | 稳定、分治思想、非原地排序 |
| 快速排序 | O(n log n) | O(log n) | 实际工程最快,不稳定,最坏 O (n²) |
| 堆排序 | O(n log n) | O(1) | 不稳定,基于完全二叉堆 |
| 桶排序 | O(n + k) | O(n + k) | 非比较排序,适合范围集中数据 |
| 基数排序 | O(d(n + k)) | O(n + k) | 按位排序,稳定,非比较 |
| 计数排序 | O(n + k) | O(n + k) | 非比较排序,速度极快,适用范围有限 |
说明:
- 稳定排序:相等元素排序后相对顺序不变
- 原地排序:不需要额外开辟大量数组空间
一、冒泡排序(Bubble Sort)
核心思想
两两比较相邻元素,每一轮把最大 / 最小值 “冒泡” 到末尾,逐步缩小未排序区间。
Go 实现
func BubbleSort(array []int) []int {
n := len(array)
for i := 0; i < n; i++ {
swap := false // 优化:如果一轮没交换,说明已有序,直接退出
for j := n - 1; j > i; j-- {
if array[j] < array[j-1] {
array[j], array[j-1] = array[j-1], array[j]
swap = true
}
}
if !swap {
break
}
}
return array
}
特点
- 时间复杂度:O(n²)
- 空间复杂度:O(1)
- 稳定排序
- 适合小规模、近乎有序的数据
二、选择排序(Selection Sort)
核心思想
每一轮在未排序区间里找到最值,直接放到已排序区间的末尾。
Go 实现
func SelectSort(arr []int) []int {
n := len(arr)
for i := 0; i < n-1; i++ {
minIdx := i
// 寻找最小值下标
for j := i + 1; j < n; j++ {
if arr[j] < arr[minIdx] {
minIdx = j
}
}
// 交换
arr[i], arr[minIdx] = arr[minIdx], arr[i]
}
return arr
}
特点
- 时间复杂度:O(n²)
- 空间复杂度:O(1)
- 不稳定排序
- 交换次数极少,移动数据成本低
三、插入排序(Insertion Sort)
核心思想
像整理扑克牌一样,将当前元素插入到前面已经有序的序列中。
Go 实现
func InsertSort(array []int) []int {
n := len(array)
for i := 1; i < n; i++ {
// 当前需要插入的数
key := array[i]
j := i - 1
// 比 key 大的元素后移
for j >= 0 && key < array[j] {
array[j+1] = array[j]
j--
}
// 插入位置
array[j+1] = key
}
return array
}
特点
- 时间复杂度:O(n²)
- 空间复杂度:O(1)
- 稳定排序
- 近乎有序数据效率极高,实际中比冒泡 / 选择快很多
四、希尔排序(Shell Sort)
核心思想
插入排序的升级版。把数组按增量(gap)分组,每组做插入排序;逐步缩小增量,最后 gap=1 时就是普通插入排序。
Go 实现
func ShellSort(arr []int) {
n := len(arr)
// 初始增量为长度一半,逐步减半
for gap := n / 2; gap > 0; gap /= 2 {
// 对每个分组做插入排序
for i := gap; i < n; i++ {
temp := arr[i]
j := i
for j >= gap && arr[j-gap] > temp {
arr[j] = arr[j-gap]
j -= gap
}
arr[j] = temp
}
}
}
特点
- 时间复杂度:O(n¹·³)
- 空间复杂度:O(1)
- 不稳定排序
- 中等规模数据效率优秀
五、归并排序(Merge Sort)
核心思想
分治算法经典应用:
- 把数组拆分成两半
- 递归排序左右
- 合并两个有序数组
Go 实现
func MergeSort(arr []int) []int {
if len(arr) <= 1 {
return arr
}
// 分割
mid := len(arr) / 2
left := MergeSort(arr[:mid])
right := MergeSort(arr[mid:])
// 合并
return merge(left, right)
}
// 合并两个有序数组
func merge(left, right []int) []int {
result := make([]int, 0, len(left)+len(right))
i, j := 0, 0
for i < len(left) && j < len(right) {
if left[i] < right[j] {
result = append(result, left[i])
i++
} else {
result = append(result, right[j])
j++
}
}
// 追加剩余元素
result = append(result, left[i:]...)
result = append(result, right[j:]...)
return result
}
特点
- 时间复杂度:O(n log n)
- 空间复杂度:O(n)
- 稳定排序
- 缺点:需要额外空间,非原地排序
六、快速排序(Quick Sort)
核心思想
最常用、工程性能最好的排序:
- 选一个基准值 pivot
- 小的放左边,大的放右边
- 递归左右区间
递归实现
func QuickSort(arr []int) []int {
if len(arr) < 2 {
return arr
}
pivot := arr[0]
var left, right []int
for _, v := range arr[1:] {
if v <= pivot {
left = append(left, v)
} else {
right = append(right, v)
}
}
left = QuickSort(left)
right = QuickSort(right)
return append(append(left, pivot), right...)
}
非递归实现
用栈模拟递归,避免栈溢出。
func QuickSortNonRecursive(arr []int) {
if len(arr) <= 1 {
return
}
stack := []int{0, len(arr) - 1}
for len(stack) > 0 {
high := stack[len(stack)-1]
stack = stack[:len(stack)-1]
low := stack[len(stack)-1]
stack = stack[:len(stack)-1]
pi := partition(arr, low, high)
// 左区间入栈
if pi-1 > low {
stack = append(stack, low, pi-1)
}
// 右区间入栈
if pi+1 < high {
stack = append(stack, pi+1, high)
}
}
}
// 分区函数
func partition(arr []int, low, high int) int {
pivot := arr[high]
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
}
特点
- 时间复杂度:O(n log n) ,最坏 O (n²)
- 空间复杂度:O(log n)
- 不稳定排序
- 实际工程中最快的通用排序算法
七、堆排序(Heap Sort)
核心思想
利用完全二叉堆(大顶堆) 结构:
- 构建大顶堆
- 堆顶与末尾交换
- 调整堆,重复执行
Go 实现
func HeapSort(arr []int) {
n := len(arr)
// 构建最大堆
for i := n/2 - 1; i >= 0; i-- {
heapify(arr, n, i)
}
// 一个个交换堆顶
for i := n - 1; i > 0; i-- {
arr[0], arr[i] = arr[i], arr[0]
heapify(arr, i, 0)
}
}
// 维护堆结构
func heapify(arr []int, n, i int) {
largest := i
left := 2*i + 1
right := 2*i + 2
// 找最大值
if left < n && arr[left] > arr[largest] {
largest = left
}
if right < n && arr[right] > arr[largest] {
largest = right
}
// 交换并递归调整
if largest != i {
arr[i], arr[largest] = arr[largest], arr[i]
heapify(arr, n, largest)
}
}
特点
- 时间复杂度:O(n log n)
- 空间复杂度:O(1)
- 不稳定排序
- 无需递归栈,适合大数据量
八、桶排序(Bucket Sort)
核心思想
非比较排序。把数据分到多个桶里,桶内排序,最后合并。
Go 实现
func BucketSort(arr []int, numBuckets int) []int {
if len(arr) == 0 {
return arr
}
// 找最大最小值
minVal, maxVal := arr[0], arr[0]
for _, v := range arr {
if v < minVal {
minVal = v
}
if v > maxVal {
maxVal = v
}
}
// 创建桶
buckets := make([][]int, numBuckets)
// 数据入桶
for _, v := range arr {
idx := (v - minVal) * numBuckets / (maxVal - minVal + 1)
buckets[idx] = append(buckets[idx], v)
}
// 排序并合并
sortedArr := make([]int, 0, len(arr))
for _, b := range buckets {
sort.Ints(b)
sortedArr = append(sortedArr, b...)
}
return sortedArr
}
特点
- 时间复杂度:O(n + k)
- 空间复杂度:O(n + k)
- 适合数据分布均匀、范围集中的场景
九、基数排序(Radix Sort)
核心思想
按位数从低到高依次排序,每一位使用计数排序。
Go 实现
func RadixSort(arr []int) {
if len(arr) == 0 {
return
}
// 找最大值
max := arr[0]
for _, v := range arr {
if v > max {
max = v
}
}
// 按位排序
for exp := 1; max/exp > 0; exp *= 10 {
countingSortForRadix(arr, exp)
}
}
// 按某一位进行计数排序
func countingSortForRadix(arr []int, exp int) {
n := len(arr)
output := make([]int, n)
count := make([]int, 10)
// 统计位数出现次数
for i := 0; i < n; i++ {
count[(arr[i]/exp)%10]++
}
// 计算前缀和
for i := 1; i < 10; i++ {
count[i] += count[i-1]
}
// 构建输出数组
for i := n - 1; i >= 0; i-- {
idx := (arr[i] / exp) % 10
output[count[idx]-1] = arr[i]
count[idx]--
}
// 复制回原数组
copy(arr, output)
}
十、计数排序(Counting Sort)
核心思想
非比较排序。用数组下标代表值,统计出现次数,直接输出有序序列。
Go 实现
func CountingSort(arr []int) []int {
if len(arr) == 0 {
return arr
}
// 找最大值
max := arr[0]
for _, v := range arr {
if v > max {
max = v
}
}
// 统计次数
count := make([]int, max+1)
for _, v := range arr {
count[v]++
}
// 生成结果
sortedArr := make([]int, 0, len(arr))
for i := 0; i <= max; i++ {
for count[i] > 0 {
sortedArr = append(sortedArr, i)
count[i]--
}
}
return sortedArr
}
特点
- 时间复杂度:O(n + k)
- 空间复杂度:O(n + k)
- 速度极快,但只能用于范围小、非负整数场景
总结
- O (n²) 简单排序:冒泡、选择、插入(插入实际最快)
- O (n log n) 高级排序:快排、归并、堆排(快排工程首选)
- 非比较排序:桶排、基数、计数(范围有限但极快)
- 稳定排序:冒泡、插入、归并、基数、计数
- 原地排序:冒泡、选择、插入、希尔、快排、堆排