概念理解:
递归
递归的基本性质就是函数自调用,处理问题时,递归是将一个大问题不断的化为小问题进行处理推导的过程。
回溯
利用了递归的性质,从问题的开头出发,不断地尝试,回一步或者是多步在做选择,直到到达终点的一个过程。
递归分析
递归算法是调用自身函数的一中算法(二叉树遍历中得到了广泛应用),可以简单理解,算法是已经实现了解决问题的步骤,使用递归只是将一个大问题不断的变小,直到得出结果。
- 案例分析1(汉诺塔题目)
// A B C三个柱子,盘子顺序有大到小不能跌倒,将A柱上的盘子移到C柱上
function hanota(A, B, C) {
// 容错处理:A柱上盘子为空时,不做处理
if(A.length <= 0) return C;
// 将 A 上的 n 个盘子移到 C 上
function move(A, B, C, n) {
if(n === 0) return; // 当前柱子上的盘子移动完成
move(A, C, B, n - 1); // 借助B,先将 n-1 个盘子移动到 A 上
C.push(A.pop()); // 将第 n 个盘子从 A 移到 C 上
move(B, A, C, n - 1); // 将 n-1 个盘子从 B 移到 C 上(递归处理)
}
return move(A, B, C, A.length);
}
- 案例分析2(中心对称数,详见 LeetCode 247)
/**
* 题目分析:
* n = 0,返回 [""]
* n = 1,返回 ["1", "0", "8"]
* n = 2,返回 ["11", "69", "88", "96"]
* n = ...
* 以此类推,可见:根据n的值进行判断,满足条件时,只需要在 n-2 的中心对称数上添加
* ["11", "69", "88", "96"] 即可。
*/
function symmetryNUm(n) {
// 1. 判断输入是否合法
if(n < 0) return throw Error("输入有误");
// 2. 确定递归结束边界条件
if(n == 0) return [""];
if(n == 1) return ["1", "0", "8"];
// 3. 将大问题小化(即为递归处理)
var list = symmetryNum(n - 2);
// 4. 整合结果
var res = [];
for(var i = 0; i < list.length; i++) {
var temp = list[i];
res.push("1" + temp + "1");
res.push("6" + temp + "9");
res.push("8" + temp + "8");
res.push("9" + temp + "6");
}
return res;
}
总结:
递归步骤:案例2中的解题步骤即为递归步骤;
回溯分析
回溯即为试探算法,当出现非法情况时,可以回退一步或多步,然后去尝试别的路径、情况,因此要实现回溯,需保证每次需要多中可以尝试的可能。
与暴力的区别是:试探,可回退,不满足时可以终止。
解题步骤:
- 判断当前是否合法,如果非法立即终止
- 当前情况是否满足递归结束条件,如果是将结果保存起来并返回
- 当前情况下遍历所有可能出现的情况,进行下一步试探
- 递归完毕后立即回溯,(即为取消前一步进行的尝试)