算法导论Word格式文档下载.docx
- 文档编号:21663377
- 上传时间:2023-01-31
- 格式:DOCX
- 页数:14
- 大小:863.78KB
算法导论Word格式文档下载.docx
《算法导论Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《算法导论Word格式文档下载.docx(14页珍藏版)》请在冰豆网上搜索。
算法的基本操作重复执行的次数是模块n的某个函数f(n),算法的时间复杂度记为T(n)=O(f(n))表示随着模块n的增大,算法执行时间的增长率和f(n)的增长率成正比
含义:
反应随着问题规模的增长,计算量增长的速度的快慢
【证明】
对于渐近非负的任意二次函数f(n)=an^2+bn+c(a>
0且a,b,c均为常数),有f(n)=Θ(n^2)
渐近正函数渐近确界的确定只与最高阶项的幂数有关,与最高阶项的系数、低阶项、常量无关
【渐进符号】
【Θ符号】对于给定的函数g(n),记Θ(g(n))={f(n):
存在三个正常数c1,c2,n0,对于任给n≥n0,满足0≤c1g(n)≤f(n)≤c2g(n)}的函数集。
并记f(n)=Θ(g(n)),它表示f(n)是Θ(g(n))中的一个元素
直观含义:
f(n)与g(n)同阶
【O符号】对于给定的函数g(n),记O(g(n))={f(n):
存在c和n0,对于任意n≥n0,满足0≤f(n)≤cg(n)}的函数集。
并记f(n)=O(g(n)),它表示f(n)是O(g(n))中的一个元素
f(n)的阶不高于g(n)的阶
【Ω符号】对于给定的函数g(n),记Ω(g(n))={f(n):
存在c和n0,对于任意n≥n0,满足0≤cg(n)≤f(n)}的函数集。
并记f(n)=Ω(g(n)),它表示f(n)是Ω(g(n))中的一个元素
f(n)的阶不低于g(n)的阶
【如何求10亿个浮点数中最大的数和最小的数】
把n个元素分成两组:
A1={A[1],...,A[int(n/2)]}和A2={A[int(n/2)+1],...,A[n]}
分别求这两组的最大值和最小值,然后分别将这两组的最大值和最小值相比较,求出全部元素的最大值和最小值。
如果A1和A2中的元素多于两个,则再用上述方法各分为两个子集。
直至子集中元素至多两个元素为止
【找出一个无序数组里面前K个最大数】
①取数组N的前k个数建一个最小堆
②依次读入其余N-K个数与堆顶元素作比较,若小于或等于堆顶元素,不做操作,若大于,用该数替换堆顶元素并调整堆
③循环结束后形成的最小堆的所有元素即为所求的K个最大数
算法复杂度:
建堆O(k)更新堆O(n-k+(n-k)*lgk)
最终的算法复杂度为O(n+(n-k)*lgk)
【排名分析】【逆序数】
①将整个序列分成A和B两个子序列,分别递归将A和B中的元素进行归并排序
②对于子序列A中的某个数i,子序列B中的某个数j,如果i<
j,没有逆序数,如果i>
j,逆序数为子序列A中i后边元素的个数(包括i),即length(A)-i+1
③如此遍历下去,即可在归并中得到逆序数
分治策略用递归方法实现,递归的时间复杂度为O(logn)
每个递归中合并前后两个子序列的时间复杂度是O(n)
总的时间复杂度为:
T(n)=2T(n/2)+O(n)根据master定理,T(n)=O(nlogn)
归并排序的复杂度为:
T(n)=2T(n/2)+cn
=O(nlogn)
【最优化问题】给定若干个约束条件和一个目标函数,在某指定集合中求满足所有约束条件的且使得目标函数值达最大或最小的元素和相应的目标函数值,即求问题的最优值和最优解(或之一)
【可行解】指定集合中满足所有约束条件的元素(不一定唯一)
【最优解】使目标函数值取最大或最小的可行解(不一定唯一)
【最优值】在最优解处的目标函数值(唯一)
【最优性原理】给出一个最优的决策序列,每个(连续)子序列自身必须是最优的决策序列
【适用动态规划求解的问题的基本要素】具有最优子结构性质、具有重叠子问题
【动态规划算法的具体步骤】①找出问题的最优子结构
分析问题的最优解(最优值)的结构特征
②递归地定义最优值
根据最优子结构,确定最优值所满足的递归公式
③计算最优值
根据最优值的递归公式,自底向上或自顶向下地计算最优值
④构造最优解
根据求最优值时得到的最优解信息构造最优解
【动态规划与分治策略】区别:
动态规划:
分解出的子问题是重叠的
分治策略:
分解出的子问题是相互独立的
【最优子结构】问题的最优解包含着它的子问题的最优解。
即不管前面的策略如何,此后的决策必须是基于当前状态(由上一次决策产生)的最优决策
【重叠子问题】在用递归算法自顶向下解问题时,每次产生的子问题并不总是新问题,有些问题被反复计算多次。
对每个子问题只解一次,然后将其解保存起来,以后再遇到同样的问题时就可以直接引用,不必重新求解
【自底向上】通过反递归实现:
调用递归式,子问题逐步往下层递归的求解
对给定的输入符号串,从对应文法开始符号的根结点出发,自顶向下地为输入符号串建立一棵分析树
实现的关键:
根据递归公式正确确定迭代顺序(即子问题的求解顺序)
【自顶向下】通过递归实现:
直接使用循环来计算所有可能的结果,往上层逐渐累加子问题的解
从输入符号串开始,逐步进行归约,直至归约到文法的开始符号
对每个子问题标记是否计算过,同一子问题只在第一次递归调用时计算并存储结果
动态规划的式子都是状态P由状态Q1、Q2、Q3……之中选择一个或几个计算出来的形式,但是如果一直是一些状态这样递归下去,最后会无限循环的,所以每个式子一直写下去最后都会得到一些状态P是常数(递归边界)的形式。
(以上可构造一个DAG)
【自底向上】就是已经知道了所有递归边界,把所有可能的状态都算出来。
基本步骤是一个拓扑排序的过程,从所有递归边界出发,当一个状态被所有可能的下层状态更新后,就用这个状态去更新后面的状态。
直到所求的状态被彻底更新完成为止。
【自顶向下】就是不考虑整个图结构,直接从要求的状态开始展开式子,如果式子中的某个状态的值还不清楚,就递归的从这个状态展开。
递归结束后式子中的状态都被对应的值替换了,所求状态自然也就清楚了。
【编辑距离】两个字符串s1,s2的编辑距离是指把s1和s2变成相同字符串(或者将一个字符串变为另一个字符串)需要下面操作的最小次数
①把某个字符ch1变成ch2
②删除某个字符
③插入某个字符
设:
从源字符串src[1…i]到目标字符串dest[1…j]的最优编辑距离用c[i,j]来表示
子状态通过编辑距离的3种操作方式状态转移至现在状态
①插入操作将c[i,j-1]转化到c[i,j]
此时是向src[1...i]最后增加一个字符
转化方程为c[i,j]=c[i,j-1]+1
②删除操作将c[i-1,j]转化到c[i,j]
此时是将src[1...i]最后一个字符删除
转化方程为c[i,j]=c[i-1,j]+1
③替换操作将c[i-1,j-1]转化到c[i,j]
此时考虑src[i]和dest[j]是否相同,如果相同则不需要替换直接转移状态,如果不同则发生替换操作
转化方程为c[i,j]=c[i-1,j-1]
src[i]==dest[j]
c[i,j]=c[i-1,j-1]+1src[i]!
=dest[j]
而当前状态c[i,j]到底是从哪种子状态转移得来的,就要比较哪种是最优的选择,即比较这三种转化的最小距离,所以最终得到的转移方程为:
【贪心策略的基本思想】
在对问题求解时,总是做出在当前看来最好的选择,并不从整体最优考虑,所做出的选择只是在某种意义上的局部最优选择,这种选择依赖于已经做出的选择,但不依赖于未做的选择。
贪心策略不能对所有问题都得到整体最优解,但对一些问题它能产生整体最优解或者其近似解
【适用贪心策略求解的问题的基本要素】具有贪心选择性质和最优子结构性质
【贪心选择性质】所求问题的整体最优解可以通过一系列局部最优选择,即贪心选择来达到
算法中每一步选择都是当前看似最佳的选择,这种选择依赖于已经做出的选择,但不依赖于未做的选择
【最优子结构性质】问题的最优解包含着它的子问题的最优解
算法中每一次都取得了最优解(即局部最优解),要保证最后的结果最优,则必须满足全局最优解包含局部最优解
【贪心策略与动态规划】区别:
贪心策略:
局部最优选择
以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。
动态规划:
整体最优选择
以自底向上的方式进行,解各子问题
相同:
都要求问题具有最优子结构性质
【如何选择】在所给的活动集合中选出最大的相容活动子集合
先将输入的活动按结束时间非递减排序,并且优先选择最早结束的活动
然后往后依次查找结束时间最近的相容活动加入
具体操作如下:
①设已排好序的各个活动构成的集合为S(0,n+1),最大相容活动集合为A,A初始为空
②在S(0,n+1)中选择结束时间最早的活动,记为a1,A=AU{a1}
③继续选择下一个活动,该活动满足两个条件:
与先前已选择的活动相容;
是未选择的活动中最早结束的
④重复上一步直到没有活动可以选择,此时得到的A为最大相容活动集合
【理由】这种选择方法可以选出最大的相容活动子集合,为未安排活动留下尽可能多的时间,使剩余的可安排时间段极大化,以便安排尽可能多的相容活动,使最多的活动能相容地使用公共资源,得到最优解
【Prim算法】
(1)设G=(V,E)是连通无向带权图,V={1,2,…,n}
(2)置X={1}
(3)只要X是V的真子集,就作如下的贪心选择:
①选取权重最小的边(x,y),其中xX,yY
②将边(x,y)加入当前的最小生成树
③将顶点y从Y移到X中
这个过程一直进行到X=V时为止
(4)在这个过程中选取到的所有边恰好构成G的一棵最小生成树
【Kruskal算法】
(2)对G的边E按权重以非降序排列
(3)初始时输出树T={}
依次取排序表中的每条边,若加入T不形成回路,则加入T;
否则将其丢弃
(4)不断重复步骤3,直到树T包含n-1条边,算法结束
【解空间搜索的基本步骤】
①深度优先遍历
从图中某顶点v出发:
(1)访问顶点v
(2)依次从v的未被访问的邻接点出发,对图进行深度优先遍历,直至图中和v有路径相通的顶点都被访问
(3)若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止
②广度优先遍历
(1)访问顶点v;
(2)依次访问v的所有邻接点w1,w2,…,wt
(3)依次访问与w1,w2,…,wt邻接的所有未曾访问过的顶点
(4)重复步骤,直到图中所有顶点均被访问过为止
【解空间搜索与普通枚举方法之间的差别】OPEN表的排序方式不同
解空间搜索:
不需要重排OPEN表
普通枚举方法:
需要重排OPEN表
【素数环问题求解思想】
设要填写1~n共n个整数,由于每个位置可以填写的情况有n种,因此,素数环问题的解空间树是一棵完全n叉树,且树的深度为n+1,因此,最坏情况下的时间复杂度为O(n^2)
这个素数环有20个位置,每个位置可以填写的整数有1~20共20种可能,可以对每个位置从1开始进行试探,利用回溯法穷举所有可能性,约束条件是正在试探的数满足如下条件:
(1)与已经填写到素数环中的整数不重复
(2)与前面相邻的整数之和是一个素数
(3)最后一个填写到素数环中的整数与第一个填写的整数之和是一个素数
在填写第k个位置时,如果满足上述约束条件,则继续填写第k+1个位置;
如果1~20个数都无法填写到第k个位置,则取消对第k个位置的填写,回溯到第k-1个位置
【基本步骤】
(1)数据初始化
(2)递归地填数
判断第i种可能是否满足三个约束条件
1如果满足:
填数
判断是否达到目标(20个已填完):
是,打印结果;
不是,递归填下一个
②如果不满足:
选择下一种可能
【优化策略】剪枝策略、展开策略
对状态进行预处理,减少搜索量,缩小搜索范围,避免无效搜索
增加约束条件,控制搜索树的深广度
【具体做法】
①使用查表法来判定素数
②由题目条件可知,相邻的两个数奇偶性必然不同,所以在选取当前元素时,先比较一下它和前一个元素的奇偶性再做决定
③由题目条件可知,如果输入n是奇数的话,一定没有满足条件的排列,所以当n是奇数时,直接返回即可
【分支界限法的基本思想】
分支界限法常以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树
首先确定目标值的上下界,边搜索边剪掉搜索树的某些枝,提高搜索效率
【分支界限法的搜索步骤】
①产生当前扩展结点的所有孩子结点
②在产生的孩子结点中,舍弃导致不可行解或导致非最优解的孩子结点
③将其余的孩子结点加入活结点表
④从活结点表中选择下一个结点作为新的扩展结点
⑤如此循环,直到找到问题的可行解(最优解)或活结点表为空
【分支界限法与回溯法】不同:
(1)求解目标:
回溯法:
找出解空间树中满足约束条件的所有解
分支界限法:
找出满足约束条件的一个解,或是在满足约束条件的解中找出在某种意义下的最优解
(2)搜索方式:
以深度优先的方式搜索解空间树
以广度优先或以最小耗费优先的方式搜索解空间树
【两种分支界限法】
(1)队列式(FIFO)分支界限法
按照队列先进先出(FIFO)原则选取下一个节点为扩展节点
(2)优先队列式分支界限法
按照优先队列中规定的优先级选取优先级最高的节点成为当前扩展节点
【设计算法用尽量短的时间查找词典中的单词一共在这篇文章中出现了多少次】
有两个问题,一个是获取英文文章中的单词,另一个是查找一个单词在不在词典中
前者是英文,直接空格和标点分词就行了
后者是数据结构,考虑到这是个词典,可以用trie来存储
算法复杂度为线性复杂度O(n)
【最快的查出所有小字符串里的字母在大字符串里都有】
①最简单的方法:
轮询小字符串里的每个字母,看它是否同在第一个字符串里。
这需要O(n*m)次操作,其中n是string1的长度,m是string2的长度。
②一个改进的方法:
对这两个字符串的字母进行排序,然后同时对两个字串依次轮询。
两个字串的排序需要(常规情况)O(mlogm)+O(nlogn)次操作,之后的线性扫描需要O(m+n)次操作。
③一个很好的方法:
只需要O(n+m)次操作。
对第一个长字串进行轮询,把其中的每个字母都放入一个Hashtable里(成本是O(n)次操作)。
然后轮询第二个字串,在Hashtable里查询每个字母,看能否找到,如果找不到,说明没有匹配成功,这将消耗掉O(m)次操作。
④一个更有趣的方法:
对一个一定个数的字母组成的字串,给每个字母分配一个素数,从2开始,往后类推。
这样A将会是2,B将会是3,C将会是5,等等。
现在先遍历第一个字串,把每个字母代表的素数相乘,会得到一个很大的整数。
然后——轮询第二个字符串,用每个字母代表的素数除它。
如果除的结果有余数,这说明有不匹配的字母,如果整个过程中没有余数,那么它是第一个字串恰好的子集了。
【给40亿个不重复的unsignedint的整数,没排序,然后再给一个数,如何快速判断这个数是否在那40亿个数当中?
】
位图法
unsignedint的取值范围是0到2^32-1,申请2^32/8=512M的内存,用每一个bit对应一个unsignedint数字。
将512M内存都初始化为0,读入40亿个数,每处理一个数字就将其对应的bit设置为1。
查询时查看相应bit值,为1表示存在,为0表示不存在
【寻找热门查询:
统计最热门的10个查询串,要求使用的内存不能超过1G】
【思路】
用Hash表统计次数,借助堆结构查找和调整/移动数据
【流程】①用Hash表统计出每个Query出现的次数
②采用堆结构找出出现次数最多的10个
【算法】
1.维护一个Key为Query字串,Value为该Query出现次数的HashTable,每次读取一个Query,如果该字串不在Table中,那么加入该字串,并且将Value值设为1;
如果该字串在Table中,那么将该字串的计数加一即可
2.维护一个大小为10的小根堆,然后遍历Query,分别和根元素进行对比
【复杂性分析】
第一步时间复杂度为O(N)
第二步时间复杂度为N'
*O(log10)
最终的时间复杂度是:
O(N)+N'
【改进】
考虑中间遴选算法的复杂度,哈希并不是最优的选择。
可以考虑后缀树或者后缀树组。
后缀树的时间复杂度为O(n),后缀数组的时间复杂度为O(nlogn),但其空间消耗更小
【英文拼写纠错:
设计一个拼写纠错的程序】
字典以字母键树组织,在用户输入同时匹配
【流程】
每输入一个字母:
沿字典树向下一层,
a)若可以顺利下行,则继续至结束,给出结果
b)若该处不能匹配,纠错处理,给出拼写建议,继续至a)
1.在字典中查找单词
字典采用27叉树组织,每个节点对应一个字母,查找就是一个字母一个字母匹配.算法时间就是单词的长度k
2.纠错算法
情况:
当输入的最后一个字母不能匹配时就提示出错,简化出错处理,动态提示可能处理方法:
(a)当前字母前缺少了一个字母:
搜索树上两层到当前的匹配作为建议
(b)当前字母拼写错误:
当前字母的键盘相邻作为提示,根据分析字典特征和用户单词已输入部分选择(a),(b)处理
【复杂性分析】影响算法的效率主要是字典的实现与纠错处理
(a)字典的实现已有成熟的算法,改进不大,也不会成为瓶颈
(b)纠错策略要简单有效,如前述情况,是线性复杂度
策略选择最是重要,可以采用统计学习的方法改进
【集合合并:
要求将其中交集不为空的集合合并,要求合并完成后的集合之间无交集】
【思路】先将集合按照大小排列后,优先考虑小的集合是否与大的集合有交集。
有就合并,如果小集合与所有其他集合都没有交集,则独立。
独立的集合在下一轮的比较中不用考虑。
这样就可以尽量减少字符串的比较次数。
当所有集合都独立的时候,就终止。
1.将集合按照大小排序,组成集合合并待处理列表
2.选择最小的集合,找出与之有交集的集合
如果有,合并之
如果无,则与其它集合是独立集合,从待处理列表中删除
3.重复直到待处理列表为空
1.将集合按照大小从小到大排序,组成待处理的集合列表
2.取出待处理集合列表中最小的集合,对于集合的每个元素,依次在其他集合中搜索是否有此元素存在:
1>
若存在,则将此小集合与大集合合并,并根据大小插入对应的位置。
转3
2>
若不存在,则在该集合中取下一个元素。
如果无下一个元素,即所有元素都不存在于其他集合。
则表明此集合独立,从待处理集合列表中删除。
并加入结果集合列表。
3>
如果待处理集合列表不为空,转2。
如果待处理集合列表为空,成功退出,则结果集合列表就是最终的输出
【算法复杂度分析】
假设集合的个数为n,最大的集合元素为m
排序的时间复杂度可以达到n*log(n)
然后对于元素在其他集合中查找,最坏情况下为(n-1)*m
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 算法 导论