递归是一种思想
递归三大要素
搞清楚这个递归函数是要干什么
对于递归,最主要的搞清楚这个递归函数是要干什么,要完成什么
//计算n的阶乘
int f(int n) {
}
寻找递归结束的条件
如果递归没有结束语句就会出现栈溢出等问题。我们需要找到当参数为什么时候递归结束,之后把参数返回。
//计算n的阶乘
int f(int n) {
if (n == 1) {
return 1;
}
}
找出函数的等价关系
我们要不断缩小参数范围,缩小之后我们可以通过一些辅助变量或者操作,使原函数的结果不变。如f(n)= f(n-1)*n
完善代码
int f(int n) {
if (n <= 2) {
return n;
}
return n * f(n-1);
}
我们来看几个需要用到递归的案例来使用递归
斐波那契数列
一个斐波那契数列的大概样式是:1、1、2、3、5、8、13、21、34…即f(n) = f(n-1) + f(n-2)
找出递归函数功能
int f(int n) {
}
找出递归结束条件
int f(int n) {
//递归结束条件
if (n <= 2) {
return 1;
}
}
找出函数的等价关系式
int f(int n) {
//递归结束条件
if (n <= 2) {
return 1;
}
//等价关系式
return f(n - 1) + f(n - 2);
}
优化:
对于上面的递归调用,如计算f(8)
可以看到在递归的过程中f(5)计算了2次,其他也都计算了多次。
我们可以将每次计算出的f(n)保存在一个数组中,下次调用时候如果计算到相同的f(n)可以直接从数组中获取,提高运行效率。
代码如下:
int f(int n) {
//递归结束条件
if (n <= 2) {
return n;
}
//判断有没有计算过
if (arr[n] != 0) {
return arr[n];
}
//等价关系式
arr[n] = f(n - 1) + f(n - 2)
return arr[n];
}
小青蛙跳台阶
小青蛙跳上一个N级台阶,它每次可以跳1级,也可以跳2级,问跳上所有台阶有多少种跳法
分析:
结束条件:当青蛙跳到最后第二格时候有两种跳法,当跳到最后一格时只有 一种跳法
等价关系:n级台阶,小青蛙第一次跳了1格,剩下n-1级有f(n-1)种跳法;
第一次跳2格,剩下n-2级台阶有f(n-2)种跳法,依次类推,知道n小于等于2。
代码如下:
int f(int n) {
//递归结束条件
if (n <= 2) {
return n;
}
//等价关系式
return f(n - 1) + f(n - 2);
反转链表
这是leetcode中的一道非常经典的题目
1、第一递归函数功能
Node reverseLiset(Node head) {
}
2、结束条件
当当前节点或者节点的下一个是空时候结束
Node reverseLiset(Node head) {
if (head == null || head.next == null) {
return head;
}
}
3、寻找等价关系
Node reverseLiset(Node head) {
if (head == null || head.next == null) {
return head;
}
Node newList = reverseLiset(head.next);
Node t1 = head.next;
t1.next = head;//由节点1指向节点2转为节点2指向节点1
head.next = null;
return newList;//将调整后的链表返回
}
不同路径
这个和小青蛙跳台阶类似。一个机器人位于一个m*n的网格上面,每次机器人只能向下或者向右移动一格,问有多少种不同的路径。
1、递归关系
机器人能向左或者向右移动后,分别有f(m-1,n)或者f(m,n-1)种路径,类似于小青蛙。以此类推。
2、结束条件
当机器人移动至m或者n为0时候,此时机器人只有一种移动方式
代码如下
public int path(int m, int n) {
if (m == 0 || n == 0) {
return 1;
}
return path(m - 1, n) + path(m, n - 1);
}
同样,这种方法也可以有优化,参考斐波那契数列的优化
private int[][] arr;
public int path(int m, int n) {
if (m == 0 || n == 0) {
return 1;
}//结束语句
//判断之前是否计算过
if (arr[m][n] != 0) {
return arr[m][n];
}
//将计算结构保存起来
arr[m][n] = path(m - 1, n) + path(m, n - 1);
return arr[m][n];//返回结果
}
时间复杂度为O(m * n),空间复杂度为O(m * n)。