05. 算法基础¶
算法效率、查找、排序与算法设计策略。
配套阅读:算法基础:课件笔记,含课程配图、例题和参考解析。
配套阅读:【软件设计】数据结构及算法应用:课件笔记,含课程配图、例题和参考解析。
本章目录¶
复杂度、查找、排序与算法策略¶
---
markmap:
height: 760
initialExpandLevel: 2
autoFit: true
colorFreezeLevel: 2
maxWidth: 280
spacingHorizontal: 80
spacingVertical: 12
nodeMinHeight: 24
paddingX: 10
---
# 算法效率与查找
## 复杂度的含义
- 时间复杂度:基本操作次数随输入规模 n 增长的趋势,区别于某次运行的实际耗时
- 空间复杂度:所需存储量随输入规模增长的趋势
- 辅助空间只计算输入以外的额外空间,通常包含递归调用栈
- O 表示渐近上界,Θ 表示渐近紧确界,Ω 表示渐近下界
- 最好、平均、最坏情形须区分;平均分析依赖输入分布
- 摊还分析:把一系列操作的总代价分摊到单次操作,不等于随机输入的平均
- 常见增长级别:常数、对数、线性、线性对数、平方、指数、阶乘
## 顺序查找
- 逐一比较关键字,找到目标或遍历结束
- 不要求数据有序,适用于顺序表与链表
- 最好 O(1),最坏 O(n)
- 等概率成功查找且从头扫描时,平均比较次数为 (n + 1) / 2
## 二分查找
- 前提:按相同比较规则有序;数组等支持随机访问的结构适合高效实现
- 闭区间 [low, high] 中取 `mid = low + (high - low) // 2`
- 比较目标与 `a[mid]`,按结果舍弃不可能包含目标的一半区间
- 找到返回;区间变空则失败;查找首个或末个相等元素需相应调整边界
- 数组实现最坏 O(log n),迭代实现辅助空间 O(1)
- 链表定位中点需要遍历,不能仅凭比较次数认定总耗时为 O(log n)
## 哈希查找
- 哈希函数把关键字映射到桶或槽位;不同关键字可能发生冲突
- 开放定址:在表内按探测序列寻找位置
- 线性探测是其中一种;删除常需墓碑标记以保留探测链
- 链地址法:同一桶内保存冲突元素的集合或链表
- 装载因子 α = 元素数 / 桶数;装载过高通常增加冲突
- 在哈希分布良好、装载受控等条件下,查找期望 O(1);最坏可达 O(n)
---
markmap:
height: 760
initialExpandLevel: 2
autoFit: true
colorFreezeLevel: 2
maxWidth: 280
spacingHorizontal: 80
spacingVertical: 12
nodeMinHeight: 24
paddingX: 10
---
# 排序:基本概念与简单排序
## 基本概念
- 稳定性:相等关键字记录的相对次序在排序后保持不变
- 原地排序:通常只需常数级辅助空间;递归栈是否计入须明确
- 时间复杂度与输入顺序、实现和数据分布有关
- 以下分析采用常见数组实现;n 为记录数,空间指辅助空间
## 直接插入排序
- 维护有序前缀,把下一个记录插入合适位置
- 大于待插入关键字的记录向后移动,相等记录不跨越即可保持稳定
- 最好 O(n),平均与最坏 O(n²),空间 O(1)
- 对短序列或基本有序的序列开销较小
## 希尔排序
- 按步长分组,对各组执行插入排序
- 逐步缩小步长,最后一次步长为 1
- 通常不稳定,空间 O(1)
- 时间复杂度依赖步长序列,不能统一写成 O(n^1.3)
- 使用逐次折半的原始步长序列时,最坏时间可达 O(n²)
## 简单选择排序
- 从未排序区间选出最小值,与区间首元素交换
- 各种输入下比较次数均为 n(n - 1) / 2,时间 Θ(n²)
- 交换次数 O(n),空间 O(1),通常不稳定
## 冒泡排序
- 逐对比较相邻元素,逆序时交换,每轮确定一个端点元素
- 相等元素不交换时稳定,空间 O(1)
- 平均与最坏 O(n²)
- 若一轮无交换就提前结束,最好 O(n)
---
markmap:
height: 760
initialExpandLevel: 2
autoFit: true
colorFreezeLevel: 2
maxWidth: 280
spacingHorizontal: 80
spacingVertical: 12
nodeMinHeight: 24
paddingX: 10
---
# 排序:堆、分治与基数
## 堆排序
- 堆是满足堆序关系的完全二叉树,可用数组保存
- 升序排序常用大顶堆,堆顶为当前最大值
- 自底向上建堆 O(n),交换堆顶与末尾后只调整剩余堆
- 共进行 O(n) 次取顶与调整,最坏时间 O(n log n)
- 常见原地、迭代下沉实现辅助空间 O(1),不稳定
## 快速排序
- 选基准并划分,使较小和较大元素进入相应区间
- 递归处理子区间;大量重复键可采用三路划分
- 划分较均衡或随机基准的期望时间 O(n log n)
- 极度不均衡时最坏 O(n²),通常不稳定
- 普通递归实现:期望栈空间 O(log n),最坏 O(n)
- 只递归较小区间、循环处理较大区间,可把栈空间限制为 O(log n)
## 二路归并排序
- 划分序列,分别排序,再合并两个有序子序列
- 合并时相等关键字优先取左侧,可保持稳定
- 常规实现各类输入的时间均为 Θ(n log n)
- 数组归并通常需要 O(n) 辅助空间;递归深度 O(log n)
## 基数排序
- 依据关键字的各位,反复分配与收集
- LSD 从最低位开始,每一轮必须稳定,才能保留前几轮的次序
- n 为记录数,d 为位数,r 为每位的基数
- 每轮采用计数分配的常见数组实现:时间 O(d(n + r)),辅助空间 O(n + r)
- 适用于能拆成有限位且每位取值范围明确的关键字
- 不属于纯比较排序,其复杂度依赖 d 和 r
## 常见实现的稳定性归类
- 稳定:直接插入、冒泡、归并、采用稳定分配的 LSD 基数排序
- 通常不稳定:简单选择、希尔、堆、原地快速排序
- 稳定性取决于实现;不能仅凭时间复杂度判断
---
markmap:
height: 760
initialExpandLevel: 2
autoFit: true
colorFreezeLevel: 2
maxWidth: 280
spacingHorizontal: 80
spacingVertical: 12
nodeMinHeight: 24
paddingX: 10
---
# 算法设计策略
## 分治
- 分解:把问题拆成规模更小的同类子问题
- 求解:递归处理子问题,足够小时直接求解
- 合并:把子问题结果组合成原问题结果
- 典型算法:归并排序、快速排序
- 二分查找只保留一个子问题,也常归入减治
## 贪心
- 每一步按既定规则作出当前最优选择,通常不回退
- 获得全局最优需要贪心选择性质与最优子结构等依据
- 典型算法:哈夫曼编码、最小生成树、满足条件的最短路径算法
- 分数背包:物品可分割时,按单位重量价值选择可得到最优解
- 0/1 背包不能普遍使用同一贪心规则
- 找零、装箱、多机调度中的贪心策略可能只是启发式,是否最优取决于具体问题
## 动态规划
- 把原问题转化为状态之间的依赖,保存并复用子问题结果
- 常见条件:最优子结构与重叠子问题
- 核心:状态定义、状态转移、初始条件、计算顺序与答案位置
- 实现:自顶向下记忆化,或自底向上递推
- 时间通常按状态数与每个状态的转移代价分析
- 典型问题:矩阵链乘法的最优括号化、0/1 背包、最长公共子序列 LCS
## 回溯
- 在解空间中深度优先尝试,遇到不满足约束的选择就回退
- 选择、递归探索、撤销选择,保证分支之间状态正确
- 用约束剪枝跳过不可能产生有效解的分支
- 可求任意一解、所有解或配合界限搜索最优解
- 典型问题:N 皇后、迷宫、排列组合、背包搜索
- 最坏复杂度常为指数级,剪枝效果依赖问题与输入