1.算法设计与分析的基本概念
1.1算法
算法(Algorithm)是对特定问题求解步骤的一种描述, 它是指令的有限序列, 其中每一条指令表示一个或多个操作。
算法具有以下5个重要特性:
1.有穷性 一个算法必须在有穷步内完成,并且每一步都在有穷时间内完成。
2.确定性 算法中每一条指令必须有确切的含义,并且只有唯一的执行路径。
3.可行性 算法中描述的操作都可通过已经实现的基本运算进行有限次完成。
4.输入 有零个或多个输出。
5.输出 有一个或多个输出,是与输入有某些特定关系的量。
1.2算法设计
设计一个 “好 ” 的算法应考虑多个目标, 包括正确性、 可读性、 健壮性和高效性等。
存在多种算法设计技术(也称为算法设计策略), 它们是设计算法的一般性方法。 已经证明这些技术对千设计好的算法非常有用, 在掌握了这些技术之后, 设计新的和有用的算法会变得容易。经常采用的算法设计技术主要有分治法、 动态规划法、 贪心法、 回溯法、 分支限界法、概率算法和近似算法等。
1.3算法分析
求解一个问题可能会有多种算法可以选择, 选择的主要标准首先是算法的正确性、可靠性、 简单性和易理解性, 其次是算法的时间复杂度和空间复杂度要低。
算法分析是指对一个算法所需要的资源进行估算, 这些资源包括内存、 通信带宽、 计算机硬件和时间等, 所需要的资源越多, 该算法的复杂度就越高。
对千任何给定的问题, 设计出复杂度尽可能低的算法是设计算法时追求的重要目标。 另一方面 ,当给定问题有很多种算法时, 选择其中复杂度最低者是一个重要准则。
1.4算法表示
常用的表示算法的方法有自然语言、 流程图、 程序设计语言和伪代码等。
(1)自然语言。其最大的优点是容易理解, 缺点是容易出现二义性, 并且算法通常很冗长。
(2)流程图。其优点是直观易懂 , 缺点是严密性不如程序设计语言, 灵活性不如自然语言。
(3)程序设计语言。其优点是能用计算机直接执行, 缺点是抽象性差, 使算法设计者拘泥于描述算法的具体细节, 忽略了 “好” 算法和正确逻辑的重要性。 此外, 还要求算法设计者掌握程序设计语言及编程技巧。
(4) 伪代码。伪代码是介于自然语言和程序设计语言之间的方法, 它采用某一程序设计语 言的基本语法, 同时结合自然语言来表达。 计算机科学家从来没有对伪代码的书写形式达成过共识。在伪代码中, 可以采用最具表达力的、最简明扼要的方法来表达一个给定的算法。
2.算法分析
2.1算法复杂度
算法复杂度是衡量算法性能的度量标准,它描述了算法运行时间(时间复杂度)或者空间占用(空间复杂度)随着问题规模增加而增长的趋势。
2.2时间复杂度
在算法分析中, 可以建立以输入规模 n 为自变量的函数 T(n)来表示算法的时间复杂度。
根据不同的输入,将算法的时间复杂度分析分为3种情况。
(1)最佳情况。 使算法执行时间最少的输入。一般情况下, 不进行算法在最佳情况下的时间复杂度分析。应用最佳情况分析的一个例子是已经证明基于比较的排序算法的时间复杂度下限为O(nlgn) 。
(2)最坏情况。 使算法执行时间最多的输入。 一般会进行算法在最坏时间复杂度的分析, 因为最坏情况是在任何输入下运行时间的一个上限, 它给我们提供一个保障, 实际情况不会比这更糟糕。另外,对于某些算法来说, 最坏情况还是相当频繁的。 而且对于许多算法来说, 平均情况通常与最坏情况下的时间复杂度一样。
(3)平均情况。 算法的平均运行时间, 一般来说, 这种情况很难分析。举个简单的例子, 现要排序10个不同的整数, 输入就有10!种不同的情况, 平均情况的时间复杂度要考虑每一种输入及其该输入的概率。 平均情况分析可以按以下3个步骤进行:
①将所有的输入按其执行时间分类。
②确定每类输入发生的概率。
③确定每类输入的执行时间。
- 算法的时间复杂度(time complexity)是一个函数,它定性描述该算法的运行时间。这是一个代表算法输入值的字符串的长度的函数。
- 时间复杂度常用大O表示法,即去掉这个函数的低阶项和首项系数。
- 相同大小的不同输入值仍可能造成算法的执行时间不同,因此我们通常使用算法的最坏情况复杂度。
- 在实际计算中,通过计算基本操作的次数来获取时间复杂度。
常见的时间复杂度有(从小到大):
- 常数时间 O(1)
- 对数时间 O(log n)
- 线性时间 O(n)
- 线性对数时间 O(n log n)
- 平方时间 O(n2)
- 立方时间 O(n3)
- 指数时间 O(2n)
- 阶乘时间 O(n!)
2.3空间复杂度
空间复杂度:用来衡量算法执行所需的额外空间资源。
常见的空间复杂度有:
常数空间 O(1)
线性空间 O(n)
线性对数空间 O(n log n)
平方空间 O(n2)
2.4渐进符号
(1)O记号。 定义为:给定一个函数 g(n), O(g(n)) ={f{n)存在正常数 c 和 n0,使得对所有
n>=n0,有0<=f(n)<=cg(n)}
(2)Ω 记号。定义为:给定一个函数 g(n), Ω(g(n)) ={f{n)存在正常数 c 和 n0, 使得对所有n>=n0,有0<=cg(n)<=f(n)}
(3)Θ 记号。定义为:定义为:给定 个函数 g(n), 0(g(n)) ={f{n)存在正常数c1、c2和 n0,使得对所有n>=n0,有0<=c1g(n)<=f(n)<=c2g(n)}
由上述定义可知, f(n)= 0(g(n))当且仅当f(n)=O(g(n))和f(n)=Ω(g(n))。
2.5求解递归式
算法可以分为非递归形式和递归形式。非递归算法的时间复杂度分析比较简单,本节主要讨论递归算法的时间复杂度分析方法。
(1) 展开法。将递归式中等式右边的项根据递归式进行替换, 称为展开。展开后的项被再
次展开, 如此下去, 直到得到一个求和表达式, 得到结果。
(2) 主方法求解递归式。T(n)=aT(n/b)+f(n)递归式描述一种算法运行时间:它将规模为n的问题分解为a个子问题,每个子问题的规模为n/b,其中a和b都为正常数。a个子问题递归的求解,每个花费时间为T(n/b)。函数f(n)包含了问题分解和子问题解合并的代价。
- 若对某个常数 ε>0 有 f(n) = O(nlogba-ε),则 T(n) = Θ(nlogba) 。
- 若 f(n) = Θ(nlogbalgkn),则 T(n) = Θ(nlogba lgk+1n) 。
- 若对某个常数 ε>0 有 f(n) = Ω(nlogba+ε),且对某个常数 c<1 和所有足够大的 n 有 af(n/b) ≤ cf(n),则 T(n) = Θ(f(n)) 。
(3)递归树法。