L8.4 - 二分查找、排列组合与质数筛法深度解析(拓展)
L8.4 - 二分查找、排列组合与质数筛法深度解析(拓展)
🎯 教学目标
- 掌握二分查找核心原理:理解边界控制与时间复杂度
- 精通排列组合生成算法:实现高效无重复的排列组合
- 理解质数筛法机制:掌握埃氏筛与欧拉筛的优化方法
- 开发高效算法思维:应对复杂问题的解决方案
📚 核心算法原理
一、二分查找(Binary Search)
1. 算法框架
1 | template<typename T> |
2. 关键要点
- 前提条件:有序数组(单调性)
- 时间复杂度:O(log n)
- 边界陷阱:
- 终止条件
left <= right
- 中间值计算防溢出
- 边界更新 ±1 的重要性
- 终止条件
二、排列组合算法
1. 全排列(Permutations)
1 | void permute(vector<int>& nums, int start, vector<vector<int>>& res) { |
2. 组合(Combinations)
1 | void combine(vector<int>& path, int start, int n, int k, vector<vector<int>>& res) { |
三、质数筛法
1. 埃拉托斯特尼筛法(Sieve of Eratosthenes)
1 | vector<bool> sieve(int n) { |
2. 欧拉筛(线性筛法)
1 | vector<int> eulerSieve(int n) { |
💡 原理详解&性能优化技巧
一、排列组合:数学中的“排队”和“分组”
1. 排列(排队游戏)
概念:从一堆东西里 按顺序 选出几个排成一队,不同的顺序算不同的结果。
例子:
你有3本不同的书《A》《B》《C》,想选2本摆到书架上,能摆出多少种不同的顺序?
答案:6种
- 所有可能:AB、AC、BA、BC、CA、CB
- 计算方法:
第一个位置有3种选择,第二个位置剩下2种选择 → 3×2=6
公式:
排列总数 = n × (n-1) × (n-2) × … × (n-k+1)
(n是总数量,k是选的数量)
2. 组合(分组游戏)
概念:从一堆东西里 不管顺序 选出几个组成一组,顺序不同但内容相同算同一种结果。
例子:
从《A》《B》《C》3本书中选2本带出门,有多少种不同的带法?
答案:3种
- 所有可能:AB、AC、BC
(BA和AB算同一种,因为带出门的书不需要排序)
公式:
组合总数 = 排列总数 ÷ 顺序不同的重复数
比如:3本选2本 → 3×2 ÷ (2×1) = 3
二、质数筛法:快速找出所有质数的“筛子”
1. 埃拉托斯特尼筛法(简单版筛子)
原理:像用筛子筛沙子,留下质数,筛掉合数。
操作步骤(以找出30以内的质数为例):
- 列出2~30所有数
[2,3,4,5,6,7,8,9,10,...,30]
- 从最小的质数2开始,划掉所有2的倍数(保留2)
→ 划掉4,6,8,10,…30 - 找下一个没被划掉的数3,划掉所有3的倍数
→ 划掉9,15,21,27 - 继续找下一个数5,划掉5的倍数
→ 划掉25 - 重复直到结束,剩下的就是质数
结果:2,3,5,7,11,13,17,19,23,29
缺点:比如6会被2和3重复筛掉,效率不够高。
2. 欧拉筛法(聪明版筛子)
原理:每个合数只被它的最小质因数筛掉,避免重复筛。
操作步骤(同样找出30以内质数):
- 准备一个空盒子放质数,所有数先标记为质数
- 从2开始,把2放进质数盒子,然后划掉2×2=4
- 到3,放进盒子,划掉3×2=6、3×3=9
- 到4,发现4被划掉,划掉4×2=8
- 到5,放进盒子,划掉5×2=10、5×3=15、5×5=25
- 以此类推…
优点:每个合数只被划一次,速度更快。
核心代码逻辑:
1 | for (每个数i从2到n) { |
三、二分查找的“偷懒”方式(C++ STL版)
1. lower_bound
和 upper_bound
函数
前提:数组必须是从小到大排序好的!
作用:
lower_bound(起始地址, 结束地址, 目标值)
:
找到第一个 ≥目标值 的位置upper_bound(起始地址, 结束地址, 目标值)
:
找到第一个 **>目标值** 的位置
示例:
1 |
|
2. 判断是否存在目标值
1 | bool exists = binary_search(nums.begin(), nums.end(), target); |
3. 实际应用:快速查找区间
1 | // 在排序好的分数中查80分的人数 |
总结表格
内容 | 核心要点 |
---|---|
排列 | 顺序不同算不同结果,公式是连乘 |
组合 | 顺序不同算相同结果,公式是排列数÷顺序重复数 |
埃氏筛法 | 简单筛倍数,但会重复筛 |
欧拉筛法 | 每个合数只筛一次,效率更高 |
STL二分查找 | 用lower_bound 和upper_bound 快速定位,数组必须先排序 |
四. 排列去重优化
1 | // 在交换前增加重复判断 |
五. 筛法空间优化
1 | // 使用位操作压缩存储空间 |
🛠️ 实战应用场景
1. 游戏装备组合系统
1 | // 从n件装备中选出k件的最佳组合 |
2. 质数在加密中的应用
1 | // 生成大质数对 |
💣 常见错误分析
1. 二分查找死循环
1 | // 错误写法 |
2. 组合重复问题
1 | // 未处理重复元素导致结果重复 |
3. 筛法下标越界
1 | // 错误写法 |
📊 算法对比
算法 | 时间复杂度 | 空间复杂度 | 适用场景 |
---|---|---|---|
标准二分查找 | O(log n) | O(1) | 有序数据查询 |
全排列 | O(n!) | O(n) | 所有可能排列 |
埃氏筛法 | O(n log log n) | O(n) | 批量找质数 |
欧拉筛法 | O(n) | O(n) | 需要质数列表 |
总结归纳
- 二分查找核心
- 维护搜索区间不变性
- 注意整数溢出问题
- 灵活处理变体问题
- 排列组合要点
- 回溯算法的剪枝优化
- 处理重复元素的去重技巧
- 组合数的数学公式应用
- 质数筛法精髓
- 埃氏筛法的批量标记思想
- 欧拉筛法的线性时间复杂度
- 位操作压缩空间技巧
评论