递归和回溯
递归
一般套路
1.判断当前情况是否非法,。例如,看看当前处理的情况是否越界,是否出现了不满足条件的情况。通常,这一部分代码都是写在最前面的。
2.判断是否满足结束递归的条件。
3.将问题的规模缩小,递归调用。
4.利用在小规模问题中的答案,结合当前的数据进行整合,得出最终的答案。
代码实现
function fn(n) {
// 第一步:判断输入或者状态是否非法?
if (input/state is invalid) {
return;
}
// 第二步:判读递归是否应当结束?
if (match condition) {
return some value;
}
// 第三步:缩小问题规模
result1 = fn(n1)
result2 = fn(n2)
...
// 第四步: 整合结果
return combine(result1, result2)
}
回溯
一般套路
1.判断当前情况是否非法,如果非法就立即返回;
2.当前情况是否已经满足递归结束条件,如果是就将当前结果保存起来并返回;
3.当前情况下,遍历所有可能出现的情况并进行下一步的尝试;
4.递归完毕后,立即回溯,回溯的方法就是取消前一步进行的尝试。
代码实例
function fn(n) {
// 第一步:判断输入或者状态是否非法?
if (input/state is invalid) {
return;
}
// 第二步:判读递归是否应当结束?
if (match condition) {
return some value;
}
// 遍历所有可能出现的情况
for (all possible cases) {
// 第三步: 尝试下一步的可能性
solution.push(case)
// 递归
result = fn(m)
// 第四步:回溯到上一步
solution.pop(case)
}
}
分析时间复杂度
递归算法的时间复杂度分析
1.迭代法
2.公式法
公式法可以说是计算递归函数复杂度最方便的工具,当递归函数的时间执行函数满足如下的关系式时,我们可以利用公式法:T(n) = a×T(n/b) + f(n)。
其中,f(n) 是每次递归完毕之后额外的计算执行时间。例如,在归并排序中,每次递归处理完两边的数组后,我们需要执行合并的操作,那么这个操作的执行时间就是 f(n)。
当参数 a、b 都确定的时候,光看递归的部分,它的时间复杂度就是:O(n^logba)。
由于时间复杂度求的是上界,通过对比递归部分的时间复杂度和 f(n) 的大小关系,得出最后的整体时间复杂度。牢记以下三种情况和相应公式:
当递归部分的执行时间 nlog(b)a 大于 f(n) 的时候,最终的时间复杂度就是 O(n^logba)。
当递归部分的执行时间 nlog(b)a 小于 f(n) 的时候,最终的时间复杂度就是 f(n)。
当递归部分的执行时间 nlog(b)a 等于 f(n) 的时候,最终的时间复杂度就是 O(n^logba)logn。
哈希判重
特点:以空间换时间的算法。