引言
在前面的章节中,我们已经看到了许多用于解决不同类型问题的算法。在解决新问题之前,通常的做法是寻找当前问题与其他已解决问题之间的相似性。这有助于我们更容易地找到解决方案。
在本章中,我们将探讨算法的不同分类方法,并在后续章节中重点介绍其中的几种(贪心算法、分治算法、动态规划)。
分类
算法的分类方法有很多,以下是一些常见的分类方式:
- 实现方法
- 设计方法
- 其他分类
按实现方法分类
1、递归或迭代
递归算法是通过反复调用自身直到满足某个基础条件为止的算法。这是函数式编程语言(如 C、C++ 等)中常用的方法。
迭代算法则使用循环结构,有时还会用到栈和队列等其他数据结构来解决问题。
有些问题适合用递归解决,而有些则适合用迭代。例如,汉诺塔问题用递归实现更容易理解。每个递归版本都有对应的迭代版本,反之亦然。
2、过程式或声明式(非过程式)
在声明式编程语言中,我们只需说明我们想要什么,而无需说明如何实现。而在过程式编程中,我们必须指定获取结果的确切步骤。例如,SQL 比过程式语言更声明式,因为查询不指定产生结果的步骤。过程式语言的例子包括:C、PHP 和 PERL。
3、串行、并行或分布式
一般来说,在讨论算法时,我们假设计算机一次执行一条指令。这些被称为串行算法。
并行算法利用计算机架构同时处理多条指令。它们将问题分解为子问题,并将这些子问题分配给多个处理器或线程。迭代算法通常可以并行化。
如果并行算法分布在不同的机器上,那么我们称这样的算法为分布式算法。
4、确定性或非确定性
确定性算法通过预定义的过程解决问题,而非确定性算法则通过使用启发式方法在每一步猜测最佳解决方案。
5、精确或近似
正如我们所见,对于许多问题,我们无法找到最优解。这意味着,我们能够找到最优解的算法被称为精确算法。在计算机科学中,如果我们没有最优解,我们就提供近似算法。
近似算法通常与 NP 难问题相关(更多详情请参阅复杂性类章节)。
按设计方法分类
另一种对算法进行分类的方法是根据它们的设计方法。
1、贪心法
贪心算法分阶段工作。在每个阶段,都会做出在该点看起来不错的决策,而不考虑未来的后果。通常,这意味着选择某种局部最优解。它假设局部最优选择也能构成全局最优解。
2、分治法
分治策略通过以下步骤解决问题:
-
分解:将问题分解为本身是同一类型问题的较小实例的子问题。
-
递归:递归地解决这些子问题。
-
解决:适当地组合它们的答案。
示例:归并排序和二分查找算法。
3、动态规划
动态规划(DP)和记忆化搜索协同工作。DP 与分治法的区别在于,后者子问题之间没有依赖关系,而 DP 中子问题会有重叠。通过使用记忆化(维护已解决子问题的表),DP 将许多问题的指数级复杂度降低为多项式复杂度(O(n^2)、O(n^3) 等)。
动态规划与递归的区别在于递归调用的记忆化。当子问题独立且没有重复时,记忆化无帮助,因此动态规划并非所有问题的解决方案。
通过使用记忆化(维护已解决子问题的表),动态规划将复杂度从指数级降低为多项式。
4、线性规划
在线性规划中,存在关于输入的不等式,并最大化(或最小化)输入的某个线性函数。许多问题(例如:有向图的最大流)可以用线性规划来讨论。
5、归约 [变换与征服]
在此方法中,我们通过将难题转化为已知问题来解决,而这些问题我们有渐近最优算法。在此方法中,目标是找到一个归约算法,其复杂度不被结果归约算法所主导。例如,寻找列表中中位数的选择算法涉及先对列表进行排序,然后在排序后的列表中找到中间元素。这些技术也称为变换与征服。
其他分类
1、按研究领域分类
在计算机科学中,每个领域都有自己的问题,需要高效的算法。示例:搜索算法、排序算法、合并算法、数值算法、图算法、字符串算法、几何算法、组合算法、机器学习、密码学、并行算法、数据压缩算法、解析技术等。
2、按复杂度分类
在此分类中,算法根据它们根据输入大小找到解决方案所需的时间进行分类。一些算法具有线性时间复杂度(O(n)),另一些具有指数时间复杂度,还有一些可能永远不会停止。注意,有些问题可能有多个具有不同复杂度的算法。
3、随机化算法
一些算法会随机做出选择。对于某些问题,最快的解决方案必须涉及随机性。示例:快速排序。
4、分支限界、枚举和回溯
这些在人工智能中有所应用,我们无需深入探讨。有关回溯方法,请参阅递归和回溯章节。
注意:在接下来的几章中,我们将讨论贪心算法、分治算法和动态规划设计方法。这些方法被强调是因为它们比其他方法更常用于解决问题。