一、递归定义
基本定义
- 函数自己调用自己(通俗第一印象)
- 大问题可以拆分小问题(拆分,边界)
- 大问题与小问题的关系(递归关系)
- 为什么拆分小问题?
- 小问题更容易求解
- 大问题与小问题内部结果一样
- 大问题与小问题入参不一样
- 为什么拆分小问题?
核心概念
-
终止条件或边界条件
-
递归关系:大问题与小问题的关系
例题
- 700. 二叉搜索树中的搜索
-
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */ //递归解法 class Solution { public TreeNode searchBST(TreeNode root, int val) { if(root == null){ return null; } if(root.val == val){ return root; } if(val<root.val){ return searchBST(root.left,val); }else{ return searchBST(root.right,val); } } } //迭代替换递归解法 class Solution { public TreeNode searchBST(TreeNode root, int val) { while (root != null) { if (root.val == val) { return root; } if (val < root.val) { root = root.left; } else { root = root.right; } } return null; } }
二、递归思想分类
1、缓存
- 将计算结果缓存,避免重复计算
- 使用cache缓存存答案,key就是参数,value就是答案
例题
- leedCode 509 斐波那契数列
-
//解法1:普通递归写法 class Solution { public int fib(int n) { if(n == 0){ return 0; } if( n == 1){ return 1; } return fib(n-1) + fib(n-2); } } //解法2:通过缓存减少重复计算 class Solution { Map<Integer,Integer> cache=new HashMap<>(); public int fib(int n) { if(cache.get(n) != null){ return cache.get(n); } if(n == 0){ return 0; } if( n == 1){ return 1; } int ans = fib(n-1) + fib(n-2); cache.put(n,ans); return ans; } } //解法3:通过迭代替换递归 //类似双指针,pre,next每次都从左往右移动,没移动一次就做一次计算下一个数的动作。边界是n<2 class Solution { public int fib(int n) { if( n < 2){ return n; } int pre = 0,next = 0,ans = 1; for(int i = 2 ;i<=n ; i++){ pre = next; next = ans; ans = pre + next; } return ans; } }
- 70. 爬楼梯 (青蛙跳台阶)
-
//直接用户递归求解会超时,必须用cache缓存所有答案 class Solution { Map<Integer,Integer> cache=new HashMap<>(); public int climbStairs(int n) { if(cache.get(n) != null){ return cache.get(n); } if( n == 0 ){ return 1; } if( n == 1 ){ return 1; } if( n == 2 ){ return 2; } int ans = climbStairs(n-1) + climbStairs(n-2); cache.put(n,ans); return ans; } }
-
-
-
//动态规划求解,没看懂 class Solution { public int climbStairs(int n) { int a = 1, b = 1, sum; for(int i = 0; i < n - 1; i++){ sum = a + b; a = b; b = sum; } return b; } }
2、分治
- 将一个大问题拆分成小问题,各个击破,然后将小问题的解组合起来
- 几乎等价标准的递归,唯一的区别就是将小问题的解组合起来
例题
- leedCode 98
3、回溯
- 找到所有满足某些条件的结果,不断试错,知错就改(并且问题可以用递归实现)
- 类似暴力搜索,但是比暴力搜索更高效(因为知错就改)
例题
- leedCode 22
三、递归形式的分类
直接递归(Direct Recursion)
在直接递归中,函数在其定义中直接调用自身。这是最基本和常见的形式
// 计算阶乘的直接递归示例
public class Main {
public static int factorial(int n) {
if (n == 0) {
return 1;
} else {
return n * factorial(n - 1);
}
}
public static void main(String[] args) {
int result = factorial(5);
System.out.println("Factorial of 5 is: " + result);
}
}
间接递归(Indirect Recursion)
在间接递归中,函数不直接调用自身,而是通过调用其他函数间接地调用自身。这种形式下,可能形成一个递归调用链。
// 间接递归示例:偶数和奇数的判断
public class Main {
public static boolean isEven(int n) {
if (n == 0) {
return true;
} else {
return isOdd(n - 1);
}
}
public static boolean isOdd(int n) {
if (n == 0) {
return false;
} else {
return isEven(n - 1);
}
}
public static void main(String[] args) {
System.out.println("Is 6 even? " + isEven(6));
System.out.println("Is 7 odd? " + isOdd(7));
}
}
尾递归(Tail Recursion)
尾递归是指递归函数中递归调用是函数的最后一个操作。在一些编程语言和编译器中,尾递归调用可以被优化为迭代形式,从而节省内存空间。
// 尾递归示例:计算斐波那契数列
public class Main {
public static int fibonacci(int n, int a, int b) {
if (n == 0) {
return a;
} else {
return fibonacci(n - 1, b, a + b);
}
}
public static void main(String[] args) {
int result = fibonacci(6, 0, 1);
System.out.println("Fibonacci of 6 is: " + result);
}
}
// 尾递归的迭代形式示例:计算斐波那契数列
public class Main {
public static int fibonacci(int n) {
if (n <= 1) {
return n;
}
int a = 0;
int b = 1;
int temp;
for (int i = 2; i <= n; i++) {
temp = b;
b = a + b;
a = temp;
}
return b;
}
public static void main(String[] args) {
int result = fibonacci(6);
System.out.println("Fibonacci of 6 is: " + result);
}
}
多态递归(Polymorphic Recursion)
多态递归是指函数在递归调用时采用不同的参数类型,导致函数的行为因输入数据类型的不同而不同。这种形式通常用于处理具有不同结构或数据类型的问题。
// 多态递归示例:根据不同输入类型处理列表
import java.util.List;
public class Main {
public static void processList(Object obj) {
if (obj instanceof List) {
List<?> list = (List<?>) obj;
for (Object item : list) {
processList(item);
}
} else {
System.out.println(obj);
}
}
public static void main(String[] args) {
List<Object> nestedList = List.of(1, List.of(2, 3), 4);
processList(nestedList);
}
}