一、递归定义
基本定义
- 函数自己调用自己(通俗第一印象)
- 大问题可以拆分小问题(拆分,边界)
- 大问题与小问题的关系(递归关系)
- 为什么拆分小问题?
- 小问题更容易求解
- 大问题与小问题内部结果一样
- 大问题与小问题入参不一样
- 为什么拆分小问题?
核心概念
-
终止条件或边界条件
-
递归关系:大问题与小问题的关系
例题
- 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);}
}