简答
- 什么是算法
- 算法是指解决问题的一种方法或一个过程,程序是算法用某种程序设计语言的具体实现
- 算法的性质有哪些?
- 输入:有外部提供的量作为算法的输入
- 输出:算法产生至少一个量作为输出
- 确定性:组成算法的每条指令是清晰、无歧义的
- 有限性:算法中每条指令的执行次数是有限的,每条指令的执行时间也是有限的
- 程序与算法
- 程序是算法用某种程序设计语言的具体实现,程序可以不满足有限性
- 算法设计分析的主要步骤
- 理解问题
- 选择数据结构
- 设计算法
- 证明正确性(算法正确性是对任意一个合法的输入经过有限步执行之后算法应给出正确的结果)
- 分析算法
- 设计程序
- 算法复杂性
- 算法复杂性的高低体现在该算法所需要的计算机资源的多少上,所需资源越多,改算法的复杂性越高。
- 算法的复杂性包括时间复杂性和空间复杂性
- 时间复杂性的计算
- 最坏情况时间复杂度:规模为n的所有输入实例中运行时间最长的实例运行时间
- 平均情况时间复杂度:规模为n的所有输入实例运行时间概率加权和
- 计量符号定义
O
:渐近上界记号,O(g(N)) = { f(N) | 存在正常数C和N0使得对所有N≥N0有:0 ≤ f(N) ≤ Cg(N) } , 记为f(N) =O(g(N))
,则称f(N)的阶不高于g(N)的阶 asN→∞
Ω
:渐近下界记号,Ω(g(N)) = { f(N) | 存在正常数C和N0使得对所有N≥N0有:0 ≤ Cg(N)≤f(N) } , 记为f(N) =Ω(g(N))
,或说f(N)的阶不低于g(N)的阶 asN→∞
θ
:紧渐近界记号,f(N)=θ(g(N))当且仅当f(N)=O(g(N))且f(N)=Ω(g(N))。此时称f(N)与g(N)同阶o
:非紧上界记号,o(g(N)) = { f(N) | 对于任何正常数存在正常数C>0,存在正数N0,使得对所有N≥N0有:0 ≤ f(N)/g(N)<C } , 记为f(N) =o(g(N))
,则称f(N)的阶比g(N)低 asN→∞
- P类问题
- 是一类能够用确定的算法在多项式时间内求解的判定问题
- NP类问题
- 非确定性算法:将问题求解分为猜测和验证两个阶段,算法的猜测阶段是非确定性的,它给出问题解的一个猜测。算法的验证阶段是确定性的,它验证猜测阶段给出解的正确性
- 所有非确定性多项式时间可解的判定问题构成NP类问题
- 请描述集合覆盖问题的近似算法的基本思想
- 递归
- 递归算法:直接或间接地调用自身的算法
- 递归函数:用函数自身给出定义的函数
- 优点:结构清晰,可读性强,而且容易用数学归纳法来证明算法的正确性。因此,它为设计算法、调试程序带来很大方便
- 缺点:递归算法的运行效率较低,无论是耗费的计算时间,还是占用的存储空间,都比非递归算法要多。
- 分治法
- 基本思想
- 将一个规模为n的问题分解为k个规模较小的子问题,这些子问题相互独立且与原问题相同。递归地解决这些子问题,然后将各子问题的解合并得到原问题的解
- 特征
- 该问题的规模缩小到一定的程度就可以容易地解决
- 该问题可以分解为若干个规模较小的相同问题
- 利用该问题分解出的子问题的解可以合并为该问题的解
- 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题
- 基本步骤
- 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
- 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;
- 合并:将各个子问题的解合并为原问题的解
- 基本思想
- 动态规划
- 基本要素
- 最优子结构性质:问题的最优解包含着其子问题的最优解
- 重叠子问题性质:递归算法求解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次
- 步骤
- 找出最优解的性质,并刻画其结构特征
- 递归地定义最优值
- 以自底向上的方式计算最优值
- 根据计算最优值时得到的信息构造最优解
- 与分治法的联系与区别
- 基本思想都是将待求解问题分解成若干个子问题,先求解子问题,再结合这些子问题的解得到原问题的解。
- 区别是,适用于动态规划求解的问题经分解得到的子问题往往不是相互独立的。
- 基本要素
- 贪心算法
- 基本要素
- 最优子结构性质:问题的最优解包含了其子问题的最优解
- 贪心选择:所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择达到
- 基本思想
- 作出在当前看来最好的选择。也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择
- 步骤
- 重复执行以下操作
- 选择当前可以选择的最优解
- 将所选择的当前解加入到问题的解中去
- 与动态规划的联系与区别
- 共同点:都需要最优子结构性质,都用来求有优化问题。
- 不同点:动态规划:每一步作一个选择—依赖于子问题的解,自底向上求解
- 贪心方法:每一步作一个选择—不依赖于子问题的解,自顶向下求解
- 基本要素
- 回溯法
- 基本思想
- 确定了解空间的组织结构后,回溯法从开始节点出发,以深度优先的方式搜索整个解空间。这个开始节点称为活结点,同时成为当前的扩展节点。在当前扩展结点处,搜索向纵深方向移至一个新结点。这个新结点就成为新的活结点
- 步骤
- 重复执行以下操作
- 选择当前可以选择的最优解
- 将所选择的当前解加入到问题的解中去
- 两种解空间树
- 当所给的问题是从n个元素的集合S中找出满足某种性质的子集时,相应的解空间树称为子集树。这类子集树通常有2n个叶结点,遍历子集树需O(2n)计算时间
- 当所给的问题是确定n个元素满足某种性质的排列时,相应的解空间树称为排列树。这类排列树通常有n!个叶结点。遍历排列树需要O(n!)计算时间。
- 基本思想
- 分支限界法
- 算法框架
- 队列式分支限界法(FIFO)
- 优先队列式分支限界法
- 贪心选择
- 搜索策略
- 在扩展结点处,先生成其所有的儿子结点(分支),然后再从当前的活结点表中选择下一个扩展结点。为了有效地选择下一扩展结点,加速搜索的进程,在每一个活结点处,计算一个函数值(限界),并根据函数值,从当前活结点表中选择一个最有利的结点作为扩展结点,使搜索朝着解空间上有最优解的分支推进,以便尽快地找出一个最优解
- 步骤
- 针对所给问题,定义问题的解空间;
- 确定易于搜索的解空间结构 ;
- 以广度优先的方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索
- 与回溯法的联系与区别
- 都是一种在问题的解空间树T中搜索问题解的算法。
- 不同点:求解目标不同; 搜索方式不同; 对扩展结点的扩展方式不同; 存储空间的要求不同。
- 算法框架
计算
算法分析
- 时间复杂度计算
- 找出语句频度最大的那条语句作为基本语句
- 计算基本语句的频度得到问题规模n的某个函数f(n)
- 取其数量级用符号“O”表示
- 10 < logn < nlogn < n2 < 2n < 3n < n! <nn
- 证明
O(f) + O(g) = O(max(f, g))
- 证明
算法设计
- 二分搜索
template<class Type>
int BinarySearch(Type a[], const Type& x, int n)
{
int left = 0, right = n-1;
while(left <= right)
{
int middle = (left+right)/2
if(x == a[middle])
return middle;
if(x > a[middle)
left = middle + 1;
else
right = middle - 1;
}
return -1;
}