[排序] 10 大经典排序算法最全总结(原理 + Go 代码实现 + 复杂度分析)

12 阅读7分钟

面试必考、算法基础必掌握,一篇吃透所有经典排序

排序算法是计算机科学最基础、最重要的知识点之一,不管是面试还是工程开发,都会高频出现。这篇文章带你一次性掌握 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)非比较排序,速度极快,适用范围有限

说明:

  1. 稳定排序:相等元素排序后相对顺序不变
  2. 原地排序:不需要额外开辟大量数组空间

一、冒泡排序(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)

核心思想

分治算法经典应用:

  1. 把数组拆分成两半
  2. 递归排序左右
  3. 合并两个有序数组

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)

核心思想

最常用、工程性能最好的排序:

  1. 选一个基准值 pivot
  2. 小的放左边,大的放右边
  3. 递归左右区间

递归实现

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)

核心思想

利用完全二叉堆(大顶堆) 结构:

  1. 构建大顶堆
  2. 堆顶与末尾交换
  3. 调整堆,重复执行

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)
  • 速度极快,但只能用于范围小、非负整数场景

总结

  1. O (n²) 简单排序:冒泡、选择、插入(插入实际最快)
  2. O (n log n) 高级排序:快排、归并、堆排(快排工程首选)
  3. 非比较排序:桶排、基数、计数(范围有限但极快)
  4. 稳定排序:冒泡、插入、归并、基数、计数
  5. 原地排序:冒泡、选择、插入、希尔、快排、堆排