数据结构与算法分析——C语言描述(第2章 算法分析)
算法(algorithm)具有以下五个特征:
- 输入(input)——算法有零个或多个输入量
- 输出(output)——算法至少产生一个输出量
- 确定性(definiteness)——算法的每一条指令都有确切的定义,没有二义性
- 可行性(effectiveness)——算法的每一条指令必须足够基本,它们可以通过已经实现的基本运算执行有限次来实现
- 有穷性(finiteness)——算法必须总能在执行有限步之后终止(程序(Program)不满足此性质)
算法的分类:
- 一个精确算法(exact algorithm)总能保证求得问题的解。
- 一个启发式算法(heuristic algorithm)通过使用某种规则、简化或智能猜测来减少问题求解时间。
2.1 数学基础
利用函数间的大小关系来定义算法的相对增长率(relative rate of growth)。
定义:如果存在正常数\(c\)和\(n_0\)使得当\(N≥n_0\)时\(T(N)≤cf(N)\),则记为\(T(N)=O(f(N))\)。(此时称\(f(N)\)为\(T(N)\)的上界(upper bound))
定义:如果存在正常数\(c\)和\(n_0\)使得当\(N≥n_0\)时\(T(N)≥cg(N)\),则记为\(T(N)=Ω(g(N))\)。(此时称\(g(N)\)为\(T(N)\)的下界(lower bound))
定义:当且仅当\(T(N)=O(h(N))\)且\(T(N)=Ω(h(N))\)时,\(T(N)=Θ(h(N))\)。
定义:如果\(T(N)=O(p(N))\)且\(T(N)≠Θ(p(N))\),则\(T(N)=o(p(N))\)。
对此定义进行数学推导,有以下3条结论。
法则1:如果\(T_1(N)=O(f(N))\)且\(T_2(N)=O(g(N))\),那么
(a)\(T_1(N)+T_2(N)=max(O(f(N)),O(g(N)))\)。
(b)\(T_1(N)*T_2(N)=O(f(N)*g(N))\)。
法则2:如果\(T(N)\)是一个\(k\)次多项式,则\(T(N)=Θ(N^2)\)。
法则3:对任意常数\(k\),\(\log^kN=O(N)\)。也就是说对数增长得非常缓慢。
可以通过计算极限\(\lim_{n\to \infty}f(N)/g(N)\)来确定两个函数的相对增长率。
- 极限是\(0\):\(f(N)=o(g(N))\)。
- 极限是\(c≠0\):\(f(N)=Θ(g(N))\)。
- 极限是\(\infty\):\(g(N)=o(f(N))\)。
- 极限摆动:二者无关。
2.2 模型
假定一个模型机(有一个标准的简单指令系统 && 每条指令花费一个时间单元 && 32位 && 无限内存),今后都在此模型机上分析算法。
2.3 要分析的问题
要分析的最重要的资源一般就是运行时间。本书抛开硬件层面的差距,主要分析所使用的算法以及对该算法的输入。
其中,数据的读入一般是个瓶颈,一旦读入数据,问题就会迅速解决。我们需要使得算法足够有效而使其不致成为问题的瓶颈。
2.4 运行时间计算
一般法则
- 法则1——for循环:循环内语句的运行时间乘以迭代的次数。
- 法则2——嵌套的for循环:循环内语句的运行时间依次乘以迭代的次数。
- 法则3——顺序语句:取运行时间的最大值。
- 法则4——if/else语句:取if和else中运行时间较长者。
分析的基本策略:从内向外逐层进行分析。
书中案例分析提到一个重要概念:分治(divide-and-conquer):“分”:把问题分成两个大致相等的子问题,然后递归地对它们求解。“治”:将两个子问题的解合并到一起并可能再做些少量的附加工作,最后得到整个问题的解。
典例:二分查找、幂运算等,时间复杂度包含\(O(\log N)\)。
书中案例分析提到另一个重要概念——联机算法(on-line algorithm):只对数据进行一次扫描,一旦完成对数组的读入和处理,就不再需要记忆它了。
仅需要常量空间并以\(O(N)\)速度运行的联机算法几乎是完美的算法。