【排序算法】八大经典排序算法详解
- 一、直接选择排序(Selection Sort)
- 算法思想
- 算法步骤
- 特性分析
- 二、堆排序(Heap Sort)
- 算法思想
- 关键步骤
- 特性分析
- 三、直接插入排序(Insertion Sort)
- 算法思想
- 算法步骤
- 特性分析
- 四、希尔排序(Shell Sort)
- 算法思想
- 核心概念
- 特性分析
- 五、冒泡排序(Bubble Sort)
- 算法思想
- 优化策略
- 特性分析
- 六、快速排序(Quick Sort)
- 算法思想
- 关键步骤
- 特性分析
- 七、归并排序(Merge Sort)
- 算法思想
- 核心操作
- 特性分析
- 八、基数排序(Radix Sort)
- 算法思想
- 执行步骤
- 特性分析
- 九、排序算法对比
- 算法优缺点对比
- 算法复杂度对比
- 总结
排序算法是计算机科学中最基础且应用最广泛的核心算法之一。本文将深入解析八种经典排序算法的原理、实现方式及适用场景,帮助读者全面掌握排序算法的核心知识。
一、直接选择排序(Selection Sort)
算法思想
通过不断选择剩余元素中的最小值,将其与当前未排序部分的起始位置交换,逐步构建有序序列。
算法步骤
- 从未排序序列中找到最小元素
- 将该元素与未排序序列的起始位置元素交换
- 将已排序序列的边界向右移动一位
- 重复上述过程直到整个序列有序
def selection_sort(arr):for i in range(len(arr)):min_idx = ifor j in range(i+1, len(arr)):if arr[j] < arr[min_idx]:min_idx = jarr[i], arr[min_idx] = arr[min_idx], arr[i]
特性分析
- 时间复杂度:O(n²)(始终进行n(n-1)/2次比较)
- 空间复杂度:O(1)
- 稳定性:不稳定(交换可能破坏原有顺序)
- 适用场景:教学演示、数据量极小时使用
二、堆排序(Heap Sort)
算法思想
利用堆这种数据结构的特性,通过构建大顶堆实现排序,将最大值依次交换到数组末尾。
关键步骤
- 建堆:从最后一个非叶子节点开始调整,构建大顶堆
- 排序阶段:
- 交换堆顶与当前末尾元素
- 堆大小减1
- 调整新堆顶使其满足堆性质
def heapify(arr, n, i):largest = il = 2 * i + 1r = 2 * i + 2if l < n and arr[l] > arr[largest]:largest = lif r < n and arr[r] > arr[largest]:largest = rif largest != i:arr[i], arr[largest] = arr[largest], arr[i]heapify(arr, n, largest)def heap_sort(arr):n = len(arr)# 建堆for i in range(n//2-1, -1, -1):heapify(arr, n, i)# 排序for i in range(n-1, 0, -1):arr[i], arr[0] = arr[0], arr[i]heapify(arr, i, 0)
特性分析
- 时间复杂度:O(n log n)
- 空间复杂度:O(1)
- 稳定性:不稳定
- 优势:适合处理海量数据,不需要递归栈
- 应用场景:实时数据流处理、优先级队列实现
三、直接插入排序(Insertion Sort)
算法思想
将待排序元素逐个插入到已排序序列的适当位置,类似整理扑克牌的过程。
算法步骤
- 从第一个元素开始视为已排序序列
- 取出下一个元素,在已排序序列中从后向前扫描
- 找到第一个小于等于当前元素的节点,插入其后
- 重复步骤2-3直到所有元素处理完成
def insertion_sort(arr):for i in range(1, len(arr)):key = arr[i]j = i-1while j >=0 and key < arr[j] :arr[j+1] = arr[j]j -= 1arr[j+1] = key
特性分析
- 最佳时间复杂度:O(n)(已排序情况)
- 平均时间复杂度:O(n²)
- 空间复杂度:O(1)
- 稳定性:稳定
- 适用场景:小规模数据(n ≤ 1000)、基本有序数据
四、希尔排序(Shell Sort)
算法思想
通过将原始列表分割成多个子序列进行插入排序,随着增量逐渐减小,最终实现整体有序。
核心概念
- 增量序列:确定子序列划分方式的间隔序列(常用Hibbard增量)
- 分组插入:对每个增量间隔形成的子序列进行插入排序
def shell_sort(arr):n = len(arr)gap = n // 2while gap > 0:for i in range(gap, n):temp = arr[i]j = iwhile j >= gap and arr[j-gap] > temp:arr[j] = arr[j-gap]j -= gaparr[j] = tempgap //= 2
特性分析
- 时间复杂度:O(n^1.3) ~ O(n²)(取决于增量序列)
- 空间复杂度:O(1)
- 稳定性:不稳定
- 优势:中等规模数据高效排序
- 历史意义:第一个突破O(n²)时间复杂度的排序算法
五、冒泡排序(Bubble Sort)
算法思想
通过相邻元素的比较和交换,使较大元素逐渐"浮"到数列顶端。
优化策略
- 提前终止:设置交换标志位检测是否已有序
- 边界优化:记录最后一次交换位置,减少无效比较
def bubble_sort(arr):n = len(arr)for i in range(n):swapped = Falsefor j in range(0, n-i-1):if arr[j] > arr[j+1]:arr[j], arr[j+1] = arr[j+1], arr[j]swapped = Trueif not swapped:break
特性分析
- 最佳时间复杂度:O(n)(已排序情况)
- 平均时间复杂度:O(n²)
- 空间复杂度:O(1)
- 稳定性:稳定
- 应用价值:算法教学、简单场景应用
六、快速排序(Quick Sort)
算法思想
采用分治策略,通过选定基准元素将数组划分为两个子数组,递归排序。
关键步骤
- 基准选择:通常选第一个/中间/随机元素
- 分区操作:将小于基准的元素放在左侧,大于的放在右侧
- 递归处理:对左右子数组重复上述过程
def quick_sort(arr):if len(arr) <= 1:return arrpivot = arr[len(arr)//2]left = [x for x in arr if x < pivot]middle = [x for x in arr if x == pivot]right = [x for x in arr if x > pivot]return quick_sort(left) + middle + quick_sort(right)
特性分析
- 平均时间复杂度:O(n log n)
- 最坏时间复杂度:O(n²)(已排序数组+选择首元素为基准)
- 空间复杂度:O(log n)(递归栈深度)
- 稳定性:不稳定
- 优化方向:三数取中法、尾递归优化、小数组切换插入排序
七、归并排序(Merge Sort)
算法思想
典型的分治算法,将数组递归分割到最小单位后合并有序子数组。
核心操作
- 分割阶段:递归地将数组二分至单个元素
- 合并阶段:将两个有序数组合并为新的有序数组
def merge_sort(arr):if len(arr) > 1:mid = len(arr)//2L = arr[:mid]R = arr[mid:]merge_sort(L)merge_sort(R)i = j = k = 0while i < len(L) and j < len(R):if L[i] < R[j]:arr[k] = L[i]i += 1else:arr[k] = R[j]j += 1k += 1while i < len(L):arr[k] = L[i]i += 1k += 1while j < len(R):arr[k] = R[j]j += 1k += 1
特性分析
- 时间复杂度:O(n log n)
- 空间复杂度:O(n)
- 稳定性:稳定
- 优势:适合链表结构、外部排序
- 应用场景:大数据排序(内存不足时使用外部归并)
八、基数排序(Radix Sort)
算法思想
按位分割的非比较排序,从最低位(LSD)或最高位(MSD)开始进行多轮排序。
执行步骤
- 初始化10个桶(0-9)
- 从最低位到最高位依次进行:
- 分配:按当前位数字将元素放入对应桶
- 收集:按桶顺序将元素放回原数组
- 重复直到处理完所有位数
def radix_sort(arr):max_num = max(arr)exp = 1while max_num // exp > 0:buckets = [[] for _ in range(10)]for num in arr:buckets[(num // exp) % 10].append(num)arr = [num for bucket in buckets for num in bucket]exp *= 10return arr
特性分析
- 时间复杂度:O(nk)(k为最大位数)
- 空间复杂度:O(n+k)
- 稳定性:稳定(依赖桶排序的稳定性)
- 限制条件:仅适用于整数排序
- 优化技巧:结合计数排序、动态确定位数
九、排序算法对比
算法优缺点对比
算法 | 最佳场景 | 优势 | 局限性 | 稳定性 |
---|---|---|---|---|
直接选择排序 | 教学演示 | 实现简单 | 效率低下 | 不稳定 |
堆排序 | 内存敏感的大数据 | 无递归栈溢出风险 | 缓存局部性差 | 不稳定 |
插入排序 | 小规模有序数据 | 自适应性能好 | 大规模数据效率骤降 | 稳定 |
希尔排序 | 中等规模随机数据 | 突破O(n²)屏障 | 增量序列选择复杂 | 不稳定 |
冒泡排序 | 检测数据有序性 | 实现简单 | 效率最低 | 稳定 |
快速排序 | 通用排序场景 | 平均性能最优 | 最坏情况性能差 | 不稳定 |
归并排序 | 大数据外部排序 | 稳定且时间复杂度优 | 空间复杂度高 | 稳定 |
基数排序 | 固定范围整数排序 | 线性时间复杂度 | 数据类型限制 | 稳定 |
算法复杂度对比
总结
理解这些基础排序算法不仅是掌握数据结构的关键,更是优化实际工程问题的基础。建议读者通过可视化工具(如 Visualgo)观察算法的执行过程,并尝试自己实现不同版本的排序算法来加深理解。