目录
递归的概念总结:
1.递归的基础概念
2.递归的Java代码模板
3.递归思维要点:
题目一:斐波那契数列
题目二:爬楼梯
题目三:二叉树的最大深度
题目四:检验二叉搜索树
题目五:括号生成
我之前的博客总结
https://blog.csdn.net/yezonghui/article/details/106437881进行过递归的总结。
(模板一定要牢记,灵活使用)
public void recur(int level , int param){
// terminator
if(level > MAX_LEVEL){
return;
}
// process current logic
process(level, param);
// drill down
recur(level:level+1 , newParam);
// restore current status
}
1)不要人肉进行递归(又费时间又费脑子,还不好写程序,不如直接进行递归代码)
2)找到最近最简方法,将其拆解成可重复的问题(重复子问题)
3)数学归纳法思维
(题目链接:https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof/)
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
示例 1:
输入:n = 2
输出:1
示例 2:
输入:n = 5
输出:5
方法一:自己的方法:采用递归
Java源代码:
class Solution {
public int fib(int n) {
return (f(n));
}
public static int f(int a){
if(a ==1){
return 1;
}else if(a ==0){
return 0;
}
else{
return (f(a-1)+f(a-2))%1000000007;
}
}
}
【注意:】上述方法超时,需要进行改进或者另寻它法
方法二:改进的递归方法:由于上面的递归中会重复计算,因此我们可以把一些一些计算过的值存起来。
我们看到上面相同颜色的都是重复计算,当n越大,重复的越多,所以我们可以使用一个map把计算过的值存起来,
每次计算的时候先看map中有没有,如果有就表示计算过,直接从map中取,如果没有就先计算,计算完之后再把结果存到map中。
即:采用hashmap结构把已经计算过的值用hashmap存储下来
Java源代码:
import java.util.HashMap;
import java.util.Map;
public class fblx {
public static void main(String[] args) {
System.out.println(f(5,new HashMap<>()));
}
public static int f(int n, Map map){
if(n<2){
return n;
}
if(map.containsKey(n)){
return map.get(n);
}
int first = f(n-1,map);
map.put(n-1,first);
int second = f(n-2,map);
map.put(n-2,second);
int result = (first+second)%1000000007;
map.put(n,result);
return result;
}
}
【注意】:return 的各处返回,到底如何返回
【注意】:1)hashmap要定义为全局变量
2)每次f(n),f(n-1),f(n-2)计算出来的结果都要随时存储到hashmap中。
class Solution {
Map map = new HashMap();
public int fib(int n) {
if(n==0 ){
return 0;
}else if(n==1){
return 1;
}else{
if(map.containsKey(n))
{
return map.get(n);
}
int first = fib(n-1);
map.put(n-1,first);
int second = fib(n-2);
map.put(n-2,second);
int result = (first+second)%1000000007;
map.put(n,result);
return result;
}
}
}
(链接:https://leetcode-cn.com/problems/climbing-stairs/)
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
方法:递推公式f(n) = f(n-1) + f(n-2) (注意理解和记忆)
for(int i =3;i<=n;i++){
f3 = f1 + f2;
f2 = f3;
f1 = f2;
}
public class climbstair {
public static void main(String[] args) {
int n =5;
int f1 = 1;
int f2 =2;
int f3 = 0;
if(n<=2) {
System.out.println(n);
}
else{
for(int i =3;i<=n;i++){
f3 = f1 + f2;
f2 = f3;
f1 = f2;
}
}
System.out.println(f3);
}
}
[注意]:此处利用for循环,进行记忆。
和题目一一样,我们可以采用同样的代码来解题
class Solution {
Map map = new HashMap();
public int climbStairs(int n) {
if(n <=2){
return n;
}else{
if(map.containsKey(n)){
return map.get(n);
}
int first = climbStairs(n-1);
map.put(n-1, first);
int second = climbStairs(n-2);
map.put(n-2, second);
int result = first+second;
map.put(n,result);
return result;
}
}
}
(链接:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/)
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
思路:不要人力递归,要从数学角度和递归思想出发。
每次都是比较左右两个子树的深度,那边子树深度大,选择那边。
延续这个思想,一直递归下去。
/**
* 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 {
int l = 1;
int r =1;
public int maxDepth(TreeNode root) {
if(root == null){
return 0;
}
int l = maxDepth(root.left);
int r = maxDepth(root.right);
return Math.max(l,r)+1;
}
}
记住上面递归的代码,类似于分治的思想,牢记这好的代码套路
(链接:https://leetcode-cn.com/problems/validate-binary-search-tree/)
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树
思路:按照中序递归的想法。
思维点:每棵子树也需要满足中序,因此需要返回每次递归的结果。
即只要有某一颗子树不满足条件,立即返回false
/**
* 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 {
TreeNode prev ;
public boolean isValidBST(TreeNode root) {
if (root == null)
return true;
//访问左子树
if(!isValidBST(root.left) ){
return false;
}
//if (!isValidBST(root.left))
// return false;
//访问当前节点:如果当前节点小于等于中序遍历的前一个节点直接返回false。
if (prev != null && prev.val >= root.val)
return false;
prev = root;
//访问右子树
if(!isValidBST(root.right) ){
return false;
}
return true;
//return isValidBST(root.right);
//if (!isValidBST(root.right))
// return false;
//return true;
}
}
或者类似的代码
class Solution {
public long pre = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
return inorder(root);
}
public boolean inorder(TreeNode root){
if(root == null){
return true;
}
if(!inorder(root.left)){
return false;
}
if(pre >= root.val){
return false;
}
pre=root.val;
return inorder(root.right);
}
}
链接:https://leetcode-cn.com/problems/generate-parentheses
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例:
输入:n = 3
输出:[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
思路:对于每一个位置,有两种情况,要不生成左括号,那么生成右括号
因此可以采用递归的思想
关键:是否可以生成左括号:当左括号数量没有达到题目要求的上界
是否可以生成右括号:当右括号数量小于左括号的数量。
class Solution {
List res = new ArrayList();
public List generateParenthesis(int n) {
generate1(0,0,n,"");
return res;
}
public void generate1(int left, int right, int n, String s){
if(left == n && right == n){
res.add(s);
//System.out.println(s);
return;
}
if(left
[注意]:对于这种会生成许多中间结果,并且需要把中间结果放置在最终结果里面,
我们一般把最终结果定义为全局变量,把中间结果放在递归函数里面,充当递归函数的一个参数。
(另外注意中间结果的类型:是基本数据类型,还是引用数据类型,后续递归是否会引起中间结果的动态改变)
(题目链接https://leetcode-cn.com/problems/shu-zhi-de-zheng-shu-ci-fang-lcof/)
实现函数double Power(double base, int exponent),
求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。
思路:采用递归思想:要求Math.pow(x,n),我们先要求出Math.pow(x,n/2)
两个注意的地方:
1)n可能为负数,所以要进行预处理,当n为负数的时候,x变为1/x,n取绝对值
2)n取绝对值之后要分奇数和偶数两种情况套路
class Solution {
public double myPow(double x, int n) {
if(n<0){
x = 1/x;
n = -n;
}
return recur(x,n);
}
public double recur(double x, int n){
if(n==0){
return 1.00000;
}
double half = recur(x,n/2);
if(n%2 ==0){
return half * half;
}else{
return half*half*(double)x;
}
}
}
(题目链接:https://leetcode-cn.com/problems/search-a-2d-matrix-ii/)
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
输出:true
思路:采用不断减少搜索范围的方法:即把一个大问题慢慢变成一个小问题
我们从表格的最左下脚(视为当前元素)出发,当该元素大于目标值的时候,我们
把当前元素往上一行,反之,当当前元素小于目标值的时候,我们把当前元素向
右边移动一列。
class Solution {
boolean flag = false;
public boolean searchMatrix(int[][] matrix, int target) {
search(matrix,target,matrix.length-1,0);
return flag;
}
public void search(int[][] matrix, int target,int x,int y) {
if(x>=0 &&x< matrix.length && y>=0 && y< matrix[0].length){
if(matrix[x][y] == target){
flag = true;
}else if(matrix[x][y] > target){
search(matrix,target,x-1,y);
}else if(matrix[x][y] < target){
search(matrix,target,x,y+1);
}
}
}
}
【注意1】:我们可以设定一个flag标志,当找到了标志为true,否则一直不改变flag的值;
【注意2】:思维误区:二维数组array[x][y]和矩阵matrix[x,y]对应坐标是不一样的表示呀!!!
例如,最左下脚二维数组表示:array[array.length][0]但是用矩阵表示为:
matrix[0,array[0].length-1]