【算法设计与分析】(二)分治_更新中:二分搜索、计数、选择、最近点对、凸包、多项式乘法、矩阵乘法、主定理&递归树、傅里叶。苏大计科院研一期末复习笔记
写在前面
突然发现我这个皮肤指数不能正常显示…… 转csdn了 改完把链接贴在这里
首先,本人很菜。
其次,本文只也许够应付考试,个人使用。而且其实就是ppt内容只是我自己喜欢这样整理。虽然全力理解内容且认真书写但也可能存在错误,如有发现麻烦指正,谢谢🌹
最后,因为不知道考试怎么考,本人的复习方式是照着目录讲一遍自己的理解+写伪代码(如果来的及会再做一个综合纯享版),再做一下上课讲过的例题和作业(印象里只有主定理、FFT、网络流上课练习+分治和动规两次作业)。
文章持续更新中,链接在此:算法设计与分析 - 随笔分类 - hotaru蛍 - 博客园 (cnblogs.com),或许考前能发完也可能发不完。
祝我们好运🌹
分治
三步走:
- 分解:大问题分解为一组与大问题相似但是规模变小的子问题。
- 解决:递归求解。如果足够小直接求解。
- 合并:子问题的解组合成原问题的解。
主定理
1.定理内容(简化形式)
- “abc”: 将问题分解为a个子问题、每个问题规模是原问题的1/b、分解和合并的时间为nc
- 递归形式:T(n) = aT(n/b) + nc , a>=1 , b>=2 , c>0 ,T(1)=O(1)
- 应用:比较a和bc
- a < bc ,T(n) = O(nc)
- a = bc ,T(n) = O(nclogn)
- a > bc ,T(n) = O(nlogba)
2. 定理内容(完全态)
3. 一些例题
二分搜索
1. 问题描述
一个已排序数组a[1...n]找出元素x,存在则返回下标不存在返回-1.
2. 分治
- 分解:Left,中间元素a[mid],Right
- 解决:如果a[mid]=x则返回mid;否则根据大小判断递归求解Left或Right。如果数组为空返回-1。
- 合并:不需要额外工作。
3. 时间复杂度分析
T(n)<=T(n/2)+O(1) ,T(1)=1 → T(n)<=T(n/2)+1
T(n)<=T(n/2)+1 <=T(n/4)+2 <=... <=T(n/2k)+k
令n = 2k ,T(n)<=T(1)+logn = 1+logn
T(n) = O(logn)
4. 正确性分析(归纳假设)
计数
1. 问题描述
一个已排序数组a[1...n],返回元素x的次数。
2. 方案分析
1. 考虑直接二分
先找到x:O(logn)
→ 向左向右扫描找到左右边界确定有s个x
总复杂度:O(logn + s),最坏情况下是Θ(n)。
例:
2. 改进:找到x的左右边界(三步走)
- 分解:a[l .. r]分为a[l...m-1]和a[m...r]两个部分
- 解决,以右边界为例(左边界):
- 基本情况:l>r 返回-1
- a[m]=x,若m=n (m=0) 或者a[m+1]>x (a[m-1]<x) ,返回m
- a[m]<x : 递归 a[l...m-1]; 否则递归a[m...r]
- 合并:不需要额外工作
时间复杂度:O(logn)
选择(中位数)
1. 问题描述
找到数组a[1...n]的中位数
→ 增强归纳假设 → 找到找到数组a[1...n]的第k小的数
2. 方案分析
1.基于排序直接解决
O(nlogn)
2. 随机主元递归(不全排)
- 思路:每次随机选一个主元v,通过v将数组划分为 小于v 等于v 大于v三个部分;迭代判断k是否在v下标范围内。
- 表示如下(注意右侧第k小需要减去左侧和中间):
- 分析时间复杂度:
- 划分的时间复杂度O(n),空间复杂度O(1)
- 如果每次选的主元刚好让左侧右侧数目相等(但不可能):T(n)=T(n/2)+n → T(n) = O(n)
- 随机选取(本方法):
- 最好情况:O(n) (一次就成)
- 最坏情况:T(n) = T(n-1)+n → T(n) = O(n2)
- 平均情况:O(n) (可以证明,但没讲)
3. 进一步优化主元选择
- 主元选择步骤:
- 将数组分成五份。(n/5向下取整有可能有六份)
- 取五份子数组各自的中位数,5个。
- 选这五个中位数中的中位数作为主元v
- 复杂度分析:
- 至少有一半中位数<= v
→ 至少有 └((n/5)/2)┘ = └(n/10)┘个中位数<= v
→ 至少有3└(n/10)┘个中位数<= v - 至少有一半中位数>= v
→ 至少有 └((n/5)/2)┘ = └(n/10)┘个中位数>= v
→ 至少有3└(n/10)┘个中位数>= v - 故递归时,从最多n - 3└(n/10)┘个数中选取下一个v
- T(n):从n个元素选择第k小元素的比较次数,
T(n) <= T(└(n/5)┘) + T( n- 3└(n/10)┘) +11/5 · n
- 至少有一半中位数<= v
- T(n)<=44n
最近点对
1. 问题描述
给定⼆维平面上的 个点,找到距离最近的⼀对点
2. 方案分析
1.穷举
O(n2)
2.排序+扫描
O(nlogn)
3.分治
-
三步走:
-
分解:将空间中点构造垂线分为左右两部分
-
解决:递归求解左右两侧最近点对
-
合并:找到跨垂线的最近点对,返回三者最小的。
-
-
如何找到跨垂线的点对
- 分别找到左侧最近点对距离δ1,右侧最近点对距离δ2,min(δ1,δ2) = δ
- 故对于跨左右垂线的仅需要考虑L<δ的点
- 插播一个事实:把带宽为2δ带状区域内的点按照纵坐标从小到大排序,只需要考虑距离每个点最近的七个点的距离,故7n次计算即可得到跨左右的最近点对。
至于为什么:
- 插播一个事实:把带宽为2δ带状区域内的点按照纵坐标从小到大排序,只需要考虑距离每个点最近的七个点的距离,故7n次计算即可得到跨左右的最近点对。
- 基于这个事实,可以得到整个基于分治思想的最近点对算法(未优化版)伪代码(?)如下:
Close_pair(p1...pn) 将所有的点依据横坐标分为L和R两个部分,每个部分有n/2个点 //O(n) δ1 ← Close_pair(L) // T(n/2) δ2 ← Close_pair(R) // T(n/2) δ = min(δ1,δ2) A ← 所有距离分割线小于δ的点 //O(n) A中坐标按照纵坐标排序 //O(nlogn) 对于A中每个点计算其与后续7个点的距离,如果距离小于δ则更新δ。 return δ
- 考虑如何优化: 保持输入有序
优化后的伪代码:
Close_pair(Px,Py) 将Px左边加入Lx,Px右边加入Rx //O(n) 将Py根据横坐标加入Ly或Ry //O(n) (L1,L2) ← Close_pair(Lx,Ly) // T(n/2) (R1,R2) ← Close_pair(Rx,Ry) // T(n/2) δ ← min(d(L1,L2),d(R1,R2)) 对于Py中每个点,如果其距离分割线的距离小于δ,将其加入Ay //O(n) 对于Ay中的每个点计算其与后续7个点的距离,如果距离小于δ则更新δ和最近点对。 return 最近点对
本文作者:hotaru蛍
本文链接:https://www.cnblogs.com/hotaru-klxx/p/17948137
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)