暴力递归(DP基础、回溯、分治…)
暴力递归就是尝试
- 把问题转化为规模缩小了的同类问题的子问题
- 有明确的不需要继续进行递归的条件(base case)
- 有当得到了子问题的结果之后的决策过程
- 不记录每一个子问题的解
关键是要学会怎么去尝试,尝试是动态规划的基础
题目一、汉诺塔问题
问题描述:打印n层汉诺塔从最左边移动到最右边的全部过程。
思路分析:
- 汉诺塔需要保证大盘不能压在小盘上面,所以从最下面的盘子开始分解
- 移动最下面第
i
个盘子前,肯定要把上面i-1
个盘子移到另一根柱子上面,然后再把第i
个盘子移到目标柱子,最后把上面i-1
个盘子放回最第i
个盘子的上面就OK了 - 每个子过程的任务,都是把最下面的盘子移到目标柱子上
- 每次都保证最下面的盘子不压在小盘子上面,这样以来所有的盘子就都不违反规则
public class Hanota {
public static void hanota(int n) {
if (n > 0) {
func(n, "左","右","中");
}
}
/**
* 汉诺塔递归,表示将start柱上面的前i个盘,从start移到end
*/
public static void func(int i, String start, String end, String other) {
if (i == 1) {
// 如果只要移动一个盘,直接移动
System.out.println("Move 1 from " + start + " to " + end);
} else {
// 要移动多个盘,先把上面i - 1个盘移动到other,
func(i - 1, start, other, end);
// 再把第i个盘移动到end
System.out.println("Move " + i + " from " + start + "to" + end);
// 再把前i-1个盘移回end
func(i - 1, other, end, start);
}
}
}
总结:尝试的时候,不需要想全局是如何保证正确的,我只需要考虑在某个局部下问题如何拆解,只要保证在该局部下拆解子问题是对的,整体就是对的。
实战练习
- LeetCode原题:面试题 08.06. 汉诺塔问题 - 力扣(LeetCode)
- 难度:Esay
题目二、打印一个字符串的全部子序列,包含空串
思路分析:就列举每一种情况,某个位置的字符只有出现和不出现两种情况,每次都是分支为2的深度优先遍历
public class PrintAllSubsquence {
public static void printAllSubsquence(String str) {
char[] chs = str.toCharArray();
process(chs, 0);
}
/**
* 来到位置i,位置i的字符有要和不要两种选择
* 之前的选择,所形成的结果是str
*/
public static void process(char[] str, int i) {
if (i == str.length) {
System.out.println(String.valueOf(str));
return;
}
// 要位置i的字符
process(str, i + 1);
char temp = str[i];
str[i] = 0;
// 不要位置i的字符
process(str, i + 1);
str[i] = temp;
}
}
实战练习
-
LeetCode原题:剑指 Offer II 097. 子序列的数目 - 力扣(LeetCode)
-
难度:Hard
-
这题用暴力递归会超时😅