算法设计与分析复习(一):算法和算法分析
来源:互联网 发布:mysql 两张表合并 编辑:程序博客网 时间:2024/06/01 12:26
参考书籍:算法设计与分析——C++语言描述(第二版)
算法问题求解基础
1. 算法概述
算法(algorithm)是求解一类问题的任意一种特殊的方法。教严格的说法是,一个算法是对特定问题求解步骤的一种描述,它是指令的有限序列。
算法具有下面五个特征:
- 输入(input):算法有零个或多个输入量
- 输出(output):算法至少产生一个输出量
- 确定性(definiteness):算法的每一条指令都有确切的定义,没有二义性
- 能行性(effectiveness):算法的每一条指令必须足够基本,他们可以通过已经实现的基本运算执行有限次来实现
- 有穷性(finiteness):算法必须总能在执行有限步之后终止
概括地说,算法是由一系列明确定义的基本指令序列所描述的,求解特定问题的过程。它能够对合法的输入,在有限时间内产生所要求的输出。如果取消*有穷性限制*,则只能称为计算过程(computational procedure)欧几里德算法又称辗转相除法,用于计算两个整数$m$和$n$($0\leq m < n的最大公约数,记为gcd(m,n)$)。其计算过程是重复应用下列等式,知道$n mod m = 0$。$$gcd(m,n) = gcd(n mod m, m), 对于m>0$$式中,$n mod m$表示$n$除以$m$之后的余数。
2. 算法设计与分析
算法一般分两类:精确算法和启发式算法。一个精确算法(exact algorithm)总能保证求得问题的解。而一个启发式算法(heuristic algorithm)通过使用某种规则、简化或智能猜测来减少问题的求解时间。
算法问题求解过程:
3. 递归和归纳
递归(recursive)定义是一种直接或间接引用自身的定义方法。一个合法的递归定义包括两个部分:基础情况(base case)和递归部分。
当一个算法采用递归方式定义时便成为递归算法,一个递归算法是指直接或间接调用自身的算法。递归本质上也是一种循环的算法结构,它把较复杂的计算逐次归结为较简单情形的计算,直至归结到最简单情形的计算,并最终得到计算结果为止。
使用归纳法进行证明的过程由两部分组成:
(1)基础情况(base case)确认被证明的结论在某种\某些基础情况下是正确的
(2)归纳步骤(induction step)这一步又可分成两子步:首先进行归纳假设,假定当问题实例的规模小于某个量k时,结论成立;然后使用这个假设证明对问题规模为k的实例,结论成立。至此结论得证。
练习
1. 逆序输出正整数的各位数(递归算法求解)
2. 汉诺塔问题
3. 排序产生算法
4. 给出n! 的递归定义式,并设计一个递归函数计算n!
5. 写一个递归算法和一个迭代算法计算二项式系数:
Cmn=Cmn−1+Cm−1n−1=n!m!(n−m)!
6. 给定一个字符串s和一个字符x,编写递归算法实现下列功能:
(1)检查x是否在s中
(2)计算x在s中出现的次数
(3)删除s中所有的x
7. 写一个C++函数求解:给定正整数n,确定n是否是它所有因子之和
8. S是有n个元素的集合,S的幂集是S所有可能的子集组成的集合。例如,S=a,b,c,则S的幂集=(),(a),(b),(c),(a,b),(a,c),(b,c),(a,b,c) 。写一个C++递归函数,以S为输入,输出S的幂集。
算法分析基础
1. 算法复杂度
一个好的算法应具有一下4个重要特性
- 正确性(correctness):算法的执行结果应当满足预先规定的功能和测试要求
- 简明性(simplicity):算法应思路清晰、层次分明、容易理解、利于编码和调试
- 效率(efficiency):算法应有效使用存储空间,并具有高的时间效率
- 最优性(optimality):算法的执行时间已达到求解该类问题所需的时间下界
影响程序运行时间的因素主要有
- 程序所依赖的算法
- 问题规模和输入数据
- 计算机系统性能
算法的时间复杂度:一个算法的时间复杂度(time complexity)是指算法运行所需的时间。
算法的空间复杂度:一个算法的空间复杂度(space complexity)是指算法运行所需的存储空间。程序运行所需要的存储空间包括以下两部分:(1)固定空间需求(fixed space requirement),(2)可变空间需求(variable space requirement)。
2. 渐进表示法
- 大O记号
设函数f(n) 和g(n) 是定义在非负整数集合上的正函数,如果存在两个正常数c 和n0 ,使得当n≥n0 时,有f(n)≤cg(n) ,则记作f(n)=O(g(n)) ,称为大O记号(big Oh notation)。 Ω 记号
设函数f(n) 和g(n) 是定义在非负整数集合上的正函数,如果存在两个正常数c 和n0 ,使得当n≥n0 时,有f(n)≥cg(n) ,则记作f(n)=Ω(g(n)) ,称为Ω 记号(omega notation)。Θ 记号
设函数f(n) 和g(n) 是定义在非负整数集合上的正函数,如果存在两个正常数c1 、c2 和n0 ,使得当n≥n0 时,有c1g(n)≤f(n)≤c2g(n) ,则记作f(n)=Θ(g(n)) ,称为Θ 记号(Theta notation)。- 小o记号
f(n)=o(g(n)) 当且仅的f(n)=O(g(n)) 且f(n)≠Ω(g(n)) 。 - 算法按时间复杂度分类
算法按计算时间分为两类:凡渐进时间复杂度为多项式时间限界的算法称为多项式时间算法(polynomial time algorithm),而渐进时间复杂度为指数函数限界的算法称为指数时间算法(exponential time algorithm)
常见多项式时间算法的渐进时间复杂度之间的关系:O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n3)
常见的指数时间算法的渐进时间复杂度之间的关系为:O(2n)<O(n!)<O(nn)
3. 递推关系
递推方程
递推方程(recurrence equation)是自然数上一个函数
计算递推式通常有三种方法:迭代方法(iterating)、替换方法(substitution)和主方法(master method)。
1. 替换方法
替换方法要去首先猜测递推式的解,然后用归纳法证明。
2. 迭代方法
迭代方法的思想是扩展递推式,将递推式先转换成一个和式,得到渐进复杂度。
3. 主方法
主定理:
设
式中,
(1)若对某常数
(2)若
(3)若对某常数
练习
1. 矩阵转置
(1)设计一个C/C++程序实现一个n×m 的矩阵转置。原矩阵保存在二维数组中。
(2)使用全局变量count,改写矩阵转置程序,并运行修改后的程序,以确定改程序的程序步
(3)计算此程序的渐进时间复杂度
2. 证明:若f(n)=amnm+am−1nm−1+⋯+a1n+a0 是m 次多项式,且am>0 ,则f(n)=Ω(nm) .
3. 运用主定理求T(n)=2T(n/4)+n√,T(1)=3 的渐进界
伸展树与跳表
伸展树
二叉搜索树(binay search tree)是一颗二叉树,它要求根的左子树上的所有结点的值都小于根的值,右子树上所有的结点的值都大于根的值,并且左右子树都是二叉搜索树。
字典(dictionary)是词条的集合,词条包括关键字(key)和其他信息。字典作为一种数据结构,主要包括搜索、插入和删除等基本运算。
字典可以用二叉搜索树来表示,但该结构容易出现退化树形,使得搜索和修改的代价增大。二叉平衡树(binary balanced tree)是一种平衡搜索树,他需要在每次插入和删除元素之后,按规则重新平衡树形,使之始终保持平衡,从而限制树的高度,避免退化。
伸展树(splay tree)是一颗二叉搜索树,但要求每访问一个元素后,将最新访问的元素移到二叉搜索树的根部,从而保证经常被访问的元素靠近根节点,而较少访问的元素位于搜索树较低的层次上。所以这是一种自调整搜索树(self-adjusting search tree)。这种将一个元素移至根部的操作称为一次伸展(splay)。
一颗伸展树是一颗二叉搜索树。它的搜索、插入和删除运算的算法与普通的二叉搜索树完全相同,只是在每次运算执行后,需要紧跟一次伸展操作。伸展操作结束,伸展结点成为树的根节点。可以按下列方式来确定伸展树运算的伸展结点。
- 搜索运算:搜索成功的结点x为伸展结点;
- 插入操作:新插入的结点x为伸展结点;
- 删除操作:被删除的结点x的双亲为伸展结点;
- 若上述运算失败终止,则搜索过程中遇到的最后一个结点为伸展结点。
一次伸展操作由一组旋转(rotation)动作组成,可分为单一旋转(single rotation)和双重旋转(double rotation)两类。
设q是本次伸展的伸展结点,
单一旋转
若q是p的左孩子或者q是p的右孩子,则执行单一旋转。前者称为zig旋转(右旋转),后者称为zag旋转(左旋转)。经过一次单一旋转,树的高度并未减小,只是将伸展结点向上移了一层。
双重旋转
- 第一种双重旋转称为一字旋转。如果伸展结点q是祖父结点的左孩子的左孩子,或是其祖父结点的右孩子的右孩子时,则执行双重旋转的一字旋转。前者称为zigzig旋转,后者称为zagzag旋转。经过一次一字旋转,树的高度并未减小,只是把伸展结点q的位置向上移了两层。
- 第二种双重旋转称之为之字旋转。如果伸展结点q是祖父结点的左孩子的右孩子,或是其祖父结点的右孩子的左孩子时,则执行双重旋转的之字旋转。前者称为zigzag旋转,后者称为zagzig旋转。经过一次之字旋转,树的高度减少1,且伸展结点q的位置上移,离根的距离减少了两层。
定义(秩):设x是伸展树T中的一个结点,
定义(势能):设x是伸展树T中的一个结点,伸展树T的势能(potential)
定义(分摊代价):设对伸展树T执行m次运算,第i次运算的分摊代价
定理:在一个有n个结点的伸展树上,执行一次运算i(搜索、插入或删除),其伸展结点为q,所需的分摊代价为:
定理:对一颗结点数目不超过n的伸展树,执行m次运算(搜索、插入或删除)的实际总代价不会超过:
跳表
跳表是一个有序链表,每个结点包含可变数目的链(指针),节点中的第i层链,跳过那些只包含低于第i层链的结点,构成一个单链表。每隔
对于一个有n个结点的跳表有如下结论:
第k层至少有一个元素的概率至多是
n/2k 。定理:跳表的高度(即最大级数)大于k的概率至多为
n/2k 。- 定理:n个元素的跳表的平均空间复杂度为
O(n) 。
- 算法设计与分析复习(一):算法和算法分析
- 算法设计与分析复习
- 算法设计与分析复习(一):习题解答
- 算法分析与设计复习小结
- 大学算法分析与设计复习总结
- 算法设计与分析复习-归纳法代码
- 算法设计与分析(一) 蛮力法
- 算法设计与分析复习-分治法算法描述
- 算法分析与设计复习-贪心算法描述
- 算法分析与设计复习概要(上)
- 算法分析与设计——递归算法(一)
- 算法设计与分析复习(二):算法设计策略-分治法
- 算法设计与分析复习(二):算法设计策略-贪心法
- 【算法复习四】计算复杂性与算法分析---算法分析
- 贪心算法(算法分析与设计)
- 概率算法(算法分析与设计)
- 算法分析与设计
- 算法设计与分析
- JVM高级特性与实践(二):对象存活判定算法(引用) 与 回收
- [计数] 美团 CodeM 复赛 排列
- Eclipse中编写JSP文件时报错 The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build P
- hadoop-2.6.1安装
- Java学习第二天
- 算法设计与分析复习(一):算法和算法分析
- 如何在 Mac 上的 IDEA 上传代码到 GitHub
- JDBC连接Oracle12c
- 基于linux2.6.31.14内核自己写虚拟摄像头驱动myvivi.c
- 【C语言】函数
- nohup-真正的Shell后台运行
- Android:写一个专注的编译时注解框架——ContentViewAnnotation
- 计算机组成原理与机构期末复习的概念
- 实训--day01枚举,泛型