diff --git a/thu_dsa/chp1/chp1.md b/thu_dsa/chp1/chp1.md index 9c11e23..eaead37 100644 --- a/thu_dsa/chp1/chp1.md +++ b/thu_dsa/chp1/chp1.md @@ -188,9 +188,21 @@ _若存在函数f(n)和正的常数c1和c2,使得在渐进的条件(n >> 2)下 在有了一个可以运行的版本后,再分析已有程序的运行过程,对其进行优化,比如规避递归程序的冗余,或者利用栈模拟操作系统的函数栈,写一个非递归的代码。这整个过程就是动态规划。 -关于动态规划,主要是有几个例子 +关于动态规划,最关键的内容还是把握它的思想,并将其应用到客观世界的复杂问题中,这个在习题集中有详细的内容。其中主要是有几个例子 + 斐波拉契数 + LCS 可以查看邓公的网课和讲义,我自己也有代码实现。 + +为了把握这里的`fib()`和`lcs`相关的算法,关键在于把握它们各个版本算法之间的联系,即原来的递归版本存在什么问题,因此新的迭代版本就这个问题进行了优化。比如`lcs`算法,根据它的定义,不难给出一个递归版本的算法,但是通过对该算法进行分析,发现居然需要`O(2^n)`的时间复杂度,深入分析发现它的低效是由于大量实例的重复计算,可以注意到,在递归过程中实际上只涉及`O(m*n)`个不同的实例,一种思想是把所有这些实例都预先计算出来,然后制表备查;另一种思想则是颠倒递归计算的次序,改为自底向上迭代,即动态规划的方法。另一个方面就是需要把握各个版本的时空复杂度,主要就是利用上面`递归跟踪`和`递推公式`的方法。最后,为了求出`lcs`的具体组成而非仅仅是长度,我自己设计了一个精巧的算法,在[习题集解答](exercises.md)中有详细的说明。 + +对于`fib`的各个算法,这里就不再赘述。需要把握的要点有 + ++ 二分递归版本算法及其复杂度分析 ++ 线性递归版本算法及其复杂度分析 ++ 上述两个算法之间的联系,线性递归版本的本质乃是在`fib()`中同时返回`fib(n)`和`fib(n-1)`。 ++ `O(n)`的迭代版算法 ++ 基于优化的`power`函数的`O(logn)`的迭代版算法,它的基本公式的推导,`power`函数的几个优化版本以及它们之间的关联,时间复杂度的分析 + +类似的思路也适用于教材和习题集中的一个`countOnes`函数,它也有三个版本两个优化版本,不过这里除了它们内在的联系以及复杂度分析以外,还需要注意一下其中精巧的位运算操作。 diff --git a/thu_dsa/chp1/lcs/lcs.cpp b/thu_dsa/chp1/lcs/lcs.cpp index 0d598dc..4aceb4d 100644 --- a/thu_dsa/chp1/lcs/lcs.cpp +++ b/thu_dsa/chp1/lcs/lcs.cpp @@ -114,5 +114,3 @@ string lcsIt(string one, string two, int len1, int len2){ } return lcs; } - - diff --git a/thu_dsa/chp1/lcs/test_lcs.cpp b/thu_dsa/chp1/lcs/test_lcs.cpp index 097fdf5..56b2858 100644 --- a/thu_dsa/chp1/lcs/test_lcs.cpp +++ b/thu_dsa/chp1/lcs/test_lcs.cpp @@ -75,4 +75,3 @@ void test_lcslenRe(){ cout << "Recursive lcs test passed." << endl; cout << "Running time: " << end - begin << endl; } -