前言
- 《算法分析与设计(第5版)》——王晓东
- 前四章的学习、记录、分享
(锚点⬇)分治法 - 常见算法示例【目录】
(锚点⬇)动态规划 - 常见算法示例【目录】
(锚点⬇)贪心算法 - 常见算法示例【目录】
第 1 章 算法概述
1.1 “程序”与“算法”
- 算法是指解决问题的一种方法或一个过程。
算法是若干指令的有穷序列,满足性质: (1)输入;(2)输出;(3)确定性;(4)有限性。
“好”的算法应达到以下目标:正确性、可读性、健壮性、高效性与低存储要求。 - 程序是算法用某种程序设计语言的具体实现。
程序可以不满足算法的性质(4)。 - 区别:程序是结果,算法是手段(方法);
- 联系:算法+数据结构=应用程序。
1.2 算法复杂性
- 算法的时间复杂性T(n);算法的空间复杂性S(n)(……)
- 算法的计算复杂性概念(……)
- 算法渐近复杂性的数学表述及复杂度的计算(……)
第 2 章 递归与分治策略
2.1 “递归”与“分治”
- 直接或间接地调用自身的算法称为递归算法。
- 用函数自身给出定义的函数称为递归函数。递归函数的二要素:边界条件、递归方程。
- 分治法:就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题,直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。
- 分治与递归的关系:递归结构是循环结构的一种,也是分治思想应用最多的一种程序结构;但是不一定要使用它,关键在于是否能够写出递归公式以及是否有必要使用递归算法。
2.2 分治法的使用
-
应用分治法的前提:
- 原问题与分解成的小问题相同或相似;
- 原问题分解成的子问题可以独立求解,子问题之间没有相关性;
- 当子问题足够小时,可以直接求解(或者说,具有分解终止条件);
- 可以将子问题合并成原问题,且合并操作的复杂度不高。
-
分治法的基本思想:
- 当我们求解某些问题时,由于这些问题要处理的数据相当多,或求解过程相当复杂,使得直接求解法在时间上相当长,或者根本无法直接求出。
- 对于这类问题,我们往往先把它分解成几个子问题,找到求出这几个子问题的解法后,再找到合适的方法,把它们组合成求整个问题的解法。
- 如果这些子问题还较大,难以解决,可以再把它们分成几个更小的子问题,以此类推,直至可以直接求出解为止。
-
分治法所能解决的问题的一般特征:
- 该问题的规模缩小到一定的程度就可以容易地解决;
- 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质;
- 利用该问题分解出的子问题的解可以合并为该问题的解;
- 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。
这条特征涉及到分治法的效率,如果各子问题是不独立的,则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然也可用分治法,但一般用动态规划较好。
-
分治法的基本步骤(形式化)
divide-and-conquer(P)—分而治之 { if ( | P | <= n0) adhoc(P); //解决小规模的问题 divide P into smaller subinstances P1,P2,...,Pk; //分解问题 for (i=1,i<=k,i++) yi=divide-and-conquer(Pi); //递归的解各子问题 return merge(y1,...,yk); //将各子问题的解合并为原问题的解 }
2.3 递归与分治策略的应用示例
- 已更新 【递归】n的阶乘
- 已更新 【递归 & 动态规划】Fibonacci数列(斐波那契数列)
- 已更新 【递归】Ackerman函数(阿克曼函数)
- 已更新 【递归】全排列
- 已更新 【递归】整数划分
- 已更新 【分治】大整数乘法
- 已更新 【分治】合并排序
- 已更新 【分治】快速排序
- 已更新 【分治】二分查找
- Hanoi塔(待定)
- Strassen矩阵乘法(待定)
- 已更新 【分治】棋盘覆盖(待定)
- 已更新 【分治】线性时间选择(待定)
- 最接近点对问题(待定)
- 已更新 【分治】循环赛日程表
第 3 章 动态规划
3.0 理解多阶段决策问题……
3.1 动态规划算法
- 基本要素:
- 最优子结构性质:问题的最优解包含了子问题的最优解
- 重叠子问题性质:保存子问题的解,再次需要时进行查询
- 基本步骤:
- 找出最优解的性质,并刻划其结构特征。
- 递归地定义最优值(写出动态规划方程)。
- 以自底向上的方式计算出最优值。
- 根据计算最优值时得到的信息,构造最优解。
3.2 分治算法与动态规划算法的差异
- 与分治法类似的是:将原问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
- 与分治法不同的是:经分解的子问题往往不是相互独立的。
- 若用分治法来解,有些共同部分(子问题或子子问题)被重复计算了很多次。
- 若用动态规划来解,保存已解决的子问题的答案,在需要时再查找,避免重复计算。
3.3 备忘录方法的概念
- 用一个表格来保存已解决的子问题的答案,用的时候查表即可;
- 采用的递归方式是自顶向下,动态规划是自低向上;
- 控制结构与直接递归相同,区别在于备忘录方式为每个解过的子问题建立备忘录;
- 初始化为每个子问题的记录存入一个特殊的值,表示并未求解;在求解过程中,查看相应记录如果是特殊值,表示未求解,否则只要取出该子问题的解即可。
3.4 动态规划算法的应用示例
- 已更新 【递归 & 动态规划】Fibonacci数列(斐波那契数列)
- 已更新 【动态规划】矩阵连乘
- 最长公共子序列(待定)
- 最大子段和(待定)
- 已更新 【动态规划】0-1背包问题
- 已更新 【动态规划】最优二叉搜索树
第 4 章 贪心算法
4.1 贪心算法
- 概念:
- 贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。
- 贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择。
- 基本要素:
- 最优子结构性质:问题的最优解包含其子问题的最优解
- 贪心选择性质:整体的最优解可通过一系列局部最优解达到;每次的选择可以依赖以前作出的选择,但不能依赖于后面的选择。
4.2 贪心算法与动态规划算法的差异
- 相同点:都要求问题具有最优子结构性质。
- 不同点:
- 贪心算法自顶向下进行分解,每一步的最优解一定包含上一步的最优解,上一步之前的最优解则不保留最终得到某种意义上的局部最优解;
- 动态规划自底向上进行分解,记录所有局部最优解……(……)
4.3 贪心算法的应用示例
- 已更新 【贪心算法】活动安排问题
- 已更新 【贪心算法】一般背包问题
- 哈夫曼编码(待更新)
- 已更新 【贪心算法】单源最短路径
- 已更新 【贪心算法】最小生成树