数据结构笔记 | 清华大学邓俊辉 | 第一章

第一章 绪论

a.计算

算法要素:

  • 输入与输出、
  • 基本操作、确定性与可行性
  • 有穷性与正确性
  • 退化与鲁棒性
  • 重用性

好算法正确(处理简单的、大规模的、一般性的、退化的、合法的输入)、健壮可读效率(速度快、存储空间小)

计算成本T(n):求解规模为n的问题所需基本操作数,在规模为n的所有实例中,只关注最坏(成本最高)者

b.大O记号

T(n)定义为算法所执行基本操作的总次数。

关注T(n)的渐进上界。引入大O记号

具体地,由T(n) < = c.f(n),用f(n)代替T(n),可忽略常系数和低次项。

//常数情况
2013×2013
//包含循环情况
for(i=0; i

O(1) :常数

O(logn) 对数:此类算法非常有效,复杂度无限接近常数,可忽略常底数和常数次幂。

O(n) 线性及多项式:线性,从n到n2是编程题主要覆盖的范围。

O(2^n) 指数:无效,从多项式到指数被认为有效算法到无效算法的分水岭。指数复杂度算法无法真正应用于实际问题中,不是有效算法,甚至不能称之为算法。相应地,多项式复杂度的算法也被称作南街的(intractable)问题。

c.常见算法分析

两个任务正确性(不变性、单调性)+ 复杂度
复杂度分析:猜测+验证,迭代(级数求和),递归(递归跟踪+递推方程)
算数级数:与末项平方同阶
幂方级数:比幂次高出一阶
几何级数(a>1):与末项同阶
收敛级数:O(1)
调和级数:1+1/2+1/3+…+1/n=O(logn)
对数级数:log1+log2+…+logn=O(nlogn)

以冒泡排序为例:

void bubblesort(int A[], int n){
    for (bool sorted = false; sorted = !sorted; n--)
        for (int i=1; i A[i]){
                swap(A[i-1], A[i]);
                sorted = false;
            }
}

不变性:经k轮交换,最大的k个元素就位
单调性:经k轮交换,问题规模缩减至n-k
正确性:经至多n趟扫描后,算法必然终止且给出正确解答

d.递归及迭代

分而治之:划分为两个子问题,规模大体相当。

减而治之:划分为两个子问题,其一平凡,另一缩减。

  1. Fibonacci数:迭代

动态规划:按规模自小而大求解各子问题的过程。

int fibI(int n){
    f = 0; g = 1;
    while (0 < n--){
        g = g + f;
        f = g - f;
    }
    return g;
}

仅使用两个中间变量f和g,记录当前的一对相邻Fibonacci数。整个算法仅需线性步的迭代.

时间复杂度为O(n),空间复杂度为O(1)

  1. 最长公共子序列(可能有多个;可能有歧义)

对于序列A[0, n]和B[0, m],LSC有三种情况:
(1)n = -1或m = -1,取空序列(“”)

(2)A[n] = ‘X’ = B[m],取LSC(A[0, n),B[0, m)) + ‘X’ (减而治之)

(3)A[n] ≠ B[m],则在 LCS(A[0, n], B[0, m))与 LCS(A[0, n), B[0, m])中取更长者 (分而治之)

最好情况:O(n + m)

最坏情况:O(2^n),此时 (n = m)

与斐波那契数列一样,有大量重复的递归实例;若采用动态规划,只需O(nm)时间即可计算所有子问题,为此只需:

(1)将所有子问题假想列成一张表;

(2)颠倒计算方向,从LCS(A[0], B[0])出发依次计算所有项。

e.习题集

习题1-12

(unsigned long) -1 //在不知道最大无符号整数大小的情况下,只需将-1转换为此类型,轻松获得此类型的最大值

你可能感兴趣的:(清华大学,数据结构)