递归算法
递归算法是一种通过函数自身调用自身来解决问题的算法。在递归算法中,问题的解决方案依赖于解决更小或更简单的同类子问题的解。递归算法通常包含两个关键部分:基本情况(base case)和递归步骤(recursive step)。
基本情况是递归的终止条件,即算法不需要进一步递归就能直接给出答案的情况。它是递归函数能够停止递归并返回结果的关键。没有基本情况,递归函数将会无限调用自身,导致栈溢出错误。
递归步骤定义了如何将问题分解成更小的子问题,并通过调用函数自身来求解这些子问题。递归步骤是递归算法的核心,它确保了算法能够逐步向基本情况逼近。
递归算法解题通常有三个步骤:
1)分析问题、寻找递归:找出大规模问题与小规模问题的关系,这样通过递归使问题的规模逐渐变小。(也就是找出数学里的递归公式,后一项与前项的关系)
2)设置边界、控制递归:找出停止条件。(即程序结束的出口)
3)设计函数、确定参数:设计函数体中的操作及相关参数。 (要特别注意函数设计时的参数问题,注意形参、实参、函数名是否一致等)
1. 贪心算法(Greedy Algorithm)
贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是全局最好或最优的算法。贪心算法通常不保证得到全局最优解,但在很多情况下,它能产生很好的近似解,并且其实现简单,效率高。
特点:
- 局部最优解的选择并不一定能保证全局最优解。
- 贪心选择依赖于问题的性质,不同的问题可能需要不同的贪心策略。
- 贪心算法的时间复杂度一般较低,因为其决策过程通常很快。
例子:最短路径问题(Dijkstra算法)、最小生成树(Prim算法和Kruskal算法)等。
2. 回溯算法(Backtracking Algorithm)
回溯算法是一种通过探索所有可能的候选解来找出所有解的算法。如果候选解被确认不是一个解(或者至少不是最后一个解),回溯算法会通过在上一步进行一些变化来撤销上一步的选择,即"回溯",然后尝试其他的候选解。
特点:
- 通过递归和剪枝来减少搜索空间。
- 适用于解决组合问题、排列问题、子集问题、划分问题等。
- 可能会产生大量的重复计算(除非通过剪枝技术减少)。
例子:八皇后问题、图的着色问题、排列组合问题等。
3. 动态规划算法(Dynamic Programming, DP)
动态规划算法通常用于求解最优化问题。它将问题分解成相对简单的子问题,并存储子问题的解以避免重复计算。在求解过程中,每个子问题的解都将依赖于其子子问题的解。
特点:
- 分解子问题,存储子问题的解,避免重复计算。
- 适用于具有重叠子问题和最优子结构的问题。
- 需要定义状态变量和状态转移方程。
例子:斐波那契数列、最长公共子序列(LCS)、最短路径问题(Bellman-Ford算法)、背包问题等。
总结
- 贪心算法在每一步都做出最优选择,但不保证全局最优。
- 回溯算法通过穷举所有可能的解来找出所有解,适用于需要找出所有解的问题。
- 动态规划算法通过解决子问题并存储子问题的解来避免重复计算,适用于具有重叠子问题和最优子结构的问题。
分治(Divide and Conquer)算法,全称分而治之,是一种非常重要且常见的算法策略。其基本思想是将一个复杂的问题分解成两个或更多的相同或相似的子问题,直到这些子问题变得简单到可以直接求解为止,然后将子问题的解合并起来,从而得到原问题的解。分治算法通常基于递归实现,其实现过程可以分为分解、求解和合并三个步骤。
4、分治算法
分治算法的基本步骤
- 分解(Divide) :
- 将原问题分解为若干个规模较小、相对独立、与原问题形式相同的子问题。
- 分解时应尽量使子问题的规模大致相同,以达到更好的平衡和效率。
- 求解(Conquer) :
- 递归地求解各个子问题。
- 如果子问题的规模仍然较大,则继续分解,直到子问题变得简单到可以直接求解为止。
- 合并(Merge) :
- 将各个子问题的解合并成原问题的解。
- 合并操作的复杂度不应太高,否则会影响整个算法的效率。
分治算法的特点
- 分而治之:通过将大问题分解为小问题,降低了问题的复杂度,使得问题更容易解决。
- 递归实现:分治算法通常通过递归来实现,即函数自身调用自身来解决问题。
- 并行优化:由于分治算法生成的子问题是相互独立的,因此可以并行解决,从而进一步提高算法的效率。
分治算法的应用实例
- 归并排序 :
- 归并排序是分治算法的一个典型应用。它将数组分为两半,递归地对每一半进行排序,然后将排序好的两半合并在一起。
- 归并排序的时间复杂度为O(n log n),其中n是数组的长度。
- 二分查找 :
- 二分查找虽然不是典型的分治算法(因为它不分解原问题为多个子问题),但它也体现了分治的思想。
- 在有序数组中查找特定元素时,每次将数组分为两半,判断目标元素在哪一半中,然后递归地在那一半中查找。
- 快速排序 :
- 快速排序也是分治算法的一个应用。它选择一个基准元素,将数组分为两个子数组,一个包含所有小于基准元素的元素,另一个包含所有大于基准元素的元素。
- 然后递归地对这两个子数组进行快速排序。
- 整数乘法 :
- 在整数乘法中,可以使用分治算法来降低乘法的时间复杂度。
- 例如,将两个n位二进制整数X和Y分别拆分为A、B和C、D(其中A、C为高位部分,B、D为低位部分),则XY可以表示为(A 2^(n/2) + B)(C2^(n/2) + D),进一步展开后可以减少乘法的次数。
- 找最近点对 :
- 在点集中找到距离最近的点对时,可以使用分治算法。
- 首先将点集按照x坐标排序,并找到x坐标的中位数m,将点集分为左右两个子集。
- 递归地在左右子集中找到最近点对,并在横跨左右子集的临界区域中查找可能存在的更近的点对。
注意事项
- 分治算法的关键在于如何划分问题和如何合并解。
- 分解时应尽量使子问题的规模大致相同,以达到更好的效率。
- 合并操作的复杂度不应太高,否则会影响整个算法的效率。
- 在并行优化时,需要注意数据划分和通信开销,以确保并行效率。