Outline
1. Examples of Recursion
Factorial
Recursion God vs Recursive Calls
Handshake Problem
2. Recursion Recipe(秘诀,处方)
Base Case
Recursive Step
with Helper Method(辅助方法)
3. More examples
Fibonacci Number(斐波那契数列), The Twelve Days of Christmas, Subsequences
一、Algorithms Design Techniques
1. 递归是算法设计的工具
Recursion as a Tool for Algorithm Design
递归是一种从代码实现过渡 (transition)到问题解决的关键技术。
2. 快速算法设计
Designing Fast Algorithms
(1)Divide-and-Conquer(分治法) = Divide + Delegate (Recursion) + Combine
通过递归分解问题、委派任务,并合并结果
(2)Dynamic Programming(动态规划)= Recursion + Memoization
结合递归与记忆化技术来提高效率
3. 旅行商问题
Travelling Salesman Problem
(1)目标是寻找访问所有城市的最短路径。
(2)穷举解法(Brute-force solution)时间复杂度为 O(n!),而动态规划降低为 O(n^2 * 2^n)。
因此所有城市对的关系总共有 n^2 组合。所有子集总数: 2^n
4. Factorial
Factorial fact(n) of a number n >= 0 is defined as follows
• fact(0) = 1
• fact(n) = n * fact(n-1)
fact(n) = n * n-1 * n-2 * ··· * 3 * 2 * 1
二、Recursion
1. Recursion Code
2. Handshake Problem
n个人在房间里,每个人都与其他人握手,递归公式为 handshake(n) = handshake(n-1) + (n-1)。
基础案例为 handshake(1) = 0
public static int handshake(int n) {
// Base case
if (n <= 1) {
return 0;
}
// Recursive step
return handshake(n - 1) + (n - 1);
}
• 假设 handshake(n-1) 能计算前 n-1 个人的握手次数。
• 第 n 个人需要与其他 n-1 个人握手,因此总握手次数为 handshake(n-1) + (n-1)。
3. Recipe for Recursion
递归的步骤
递归的核心思想是将问题分解为更小的子问题,并依赖子问题(smaller instances)的解来构建原始问题的解。
(1) Base Case
• 找到可以直接解决的最简单情况。(计算阶乘时,当n = 0时,直接返回 1)
(2) Recursive Step
• 尝试从几个例子中观察递归关系(recursive relation among instances)。
• 根据通用规律对n进行公式化(formulate generally for n),并依赖递归调用来解决较小的实例。
• 确保每次递归都能最终到达base case
递归结构就像俄罗斯套娃一样,每个问题都包含一个规模更小的子问题,直到遇到最小的情况(基础案例)
4. Rabbit Breeding Problem
(1) 问题描述
• 假设一对兔子需要一个时间单位成熟并开始繁殖,每次繁殖都生出一对雄性和雌性兔子。
• 兔子永远不会死亡。
• 问题:在第n个时间单位后,兔子共有多少对?
(2) 解决方法
这实际上是斐波那契数列(Fibonacci Number)的应用。每个时间单位的兔子总数是前两期兔子总数之和:fibo(n) = fibo(n - 1) + fibo(n - 2)
(3) 递归公式
• 基础案例:
fibo(0) = 0
fibo(1) = 1
• 递归公式:
fibo(n) = fibo(n - 1) + fibo(n - 2)
5. Fibonacci Number Code
斐波那契数代码
public static int fibo(int n) {
// Base case
if (n == 0 || n == 1) {
return n;
}
// Recursive step
return fibo(n - 1) + fibo(n - 2);
}
6. The Twelve Days of Christmas Problem
问题描述:
• 在第n天,收到的礼物总数是前一天礼物总数加上当天新增的礼物数量。
• 问题:在第n天,共收到多少礼物?
递归公式:
• 基础案例:
gifts(1) = 1
• 递归公式:
gifts(n) = gifts(n-1) + n
public static int gifts(int n) {
// Base case
if (n == 1) {
return 1;
}
// Recursive step
return gifts(n - 1) + n;
}
如果 ,计算过程如下:
• 第 1 天:1 件礼物。
• 第 2 天:1(前一天)+ 2(当天)= 3 件。
• 第 3 天:3(前两天总数)+ 3(当天)= 6 件。
三、Subsequences Problem
子序列问题
给定一个由字母 A-Z 或 a-z 组成的字符串,返回所有可能的子序列(Subsequences)。
子序列是从字符串中选出的字母组成的,字母的顺序保持不变。
示例:subsequences("abc") 的输出为:abc, ab, bc, ac, a, b, c, ""。
注意点:
• 空字符串 "" 也是一个有效的子序列。
the trailing comma preceding the empty string ""
1. 每个字母可以选择加入子序列,也可以不选择。
2. 对于字符串 "abc":
• 选择第一个字母 "a":递归处理剩余字符串 "bc"。
• 不选择第一个字母:递归处理剩余字符串 "bc"。
3. 基础案例(Base Case):
• 当字符串为空时,子序列仅包含空字符串 ""。
4. 递归公式:
• 子序列 = 所有包含当前字母的子序列 + 所有不包含当前字母的子序列。
1. Helper Method
private static String subseqHelper(String partialSubseq, String word) {
if (word.isEmpty())
// base case
return partialSubseq;
else
// recursive step
return subseqHelper(partialSubseq + word.charAt(0),word.substring(1)) + ","
+ subseqHelper(partialSubseq, word.substring(1));
}