递归三要素
1. 明确函数的作用
由我们自己定义
2. 寻找递归终止条件
递归就是函数自己调用自己,当参数为什么时,我们能够直接知道函数的结果,这时递归终止,将函数值进行返回
3. 找出函数的等价关系式(等价操作步骤)
我们不断缩小参数的范围,缩小之后要通过辅助的变量或操作使原函数的结果不变
侧重于函数的功能,忽略实现步骤
- 辅助的变量(缩小参数范围+变量):适用于数字计算之类的题目
f(n)=n*f(n-1)
- 操作(缩小参数范围+操作):适用于有节点的数据结构(链表,树)
reverseList(head)等价于reverseList(head.next)+改变一下1、2两个节点的指向
3.1 经验之谈
当我们在具有节点的数据结构中使用递归时,递归返回值有两类
-
我们要返回找到的节点
直接
return 递归函数(缩小范围的参数);
因为我们函数的目的是为了找到某个节点,所以getNode(node.left, key)与getNode(node, key)是等价的,最终结果都是同一个节点//返回以node为根的二分搜索树中,键为key的节点 //返回的是当前节点 private Node getNode(Node node,K key){ //如果根为空,返回null if (node==null) return null; //key<根节点的key,去左子树中寻找,缩小参数范围 if (key.compareTo(node.key)<0) return getNode(node.left, key); //key>根节点的key if (node.key.compareTo(key)>0) return getNode(node.right, key); //key==node.kay else return node; }
-
我们要返回增删之后新的二分搜索树的根
remove(node.left)显然不与remove(node)等价,因此我们要进行一些操作,使其等价
node.left=remove(node.left);
return node;
//移除以node为根的二分搜索树中最小的节点 //返回移除后新的二分搜索树的根 private Node removeMin(Node node){ //递归终止条件 //当前节点的左子树为空,那么就为最小节点。 //可能其还有右子树,我们提取出来他的右子树,其右节点理所当然成为新的二分搜索树的根 if (node.left==null){ //保存被删节点的右子树 Node rightNode=node.right; //将被删除的节点从当前二叉树中脱离关系 node.right=null; size--; return rightNode; } //removeMin(node)等价于removeList(node.left)+改变node.left指向新的右子树的根 node.left= removeMin(node.left); return node; }
递归的优化
1. 考虑是否重复计算
使用递归可能会有很多的子问题被重复计算
我们可以把计算的结果保存起来,例如保存f(4),当再次要计算f(4)时,先判断之前是否计算过,如果计算过,直接把结果取出即可,不用重复递归计算了
用什么保存呢?可以用数组或者 HashMap 保存,我们用数组来保存把,把 n 作为我们的数组下标,f(n) 作为值,例如 arr[n] = f(n)。f(n) 还没有计算过的时候,我们让 arr[n] 等于一个特殊值,例如 arr[n] = -1。
当我们要判断的时候,如果 arr[n] = -1,则证明 f(n) 没有计算过,否则, f(n) 就已经计算过了,且 f(n) = arr[n]。直接把值取出来就行了。
// 我们实现假定 arr 数组已经初始化好的了。
int f(int n){
if(n <= 1){
return n;
}
//先判断有没计算过
if(arr[n] != -1){
//计算过,直接返回
return arr[n];
}else{
// 没有计算过,递归计算,并且把结果保存到 arr数组里
arr[n] = f(n-1) + f(n-2);
reutrn arr[n];
}
}
2. 考虑是否可以自底向上
对于递归,我们一般是从上往下递归,直到终止条件,再一层一层把值返回。而当层数太多,栈空间可能不够用
public int f(int n) {
if(n <= 2)
return n;
int f1 = 1;
int f2 = 2;
int sum = 0;
for (int i = 3; i <= n; i++) {
sum = f1 + f2;
f1 = f2;
f2 = sum;
}
return sum;
}
这种方法,其实也被称之为递推。