在看了数据结构与算法后,很重要的一步就是不断地刷题理解这些数据结构,但是一遇到递归的时候,就会感觉力不从心。在网上看了一些教程后,由于天资愚钝,还是不太理解,因此想写一个博客记录一下这个过程。
在网上看到的解决递归的问题一般是分三步走:
- 递归的终止条件:何时停止递归调用
- 本次递归要做什么:递归需要实现什么功能
- 返回值是什么:返回什么内容
不知道有没有和我一样的纠结每次递归调用结果的,可能我会想着每次查看递归的内容,层层嵌套,不好理解。
这三部曲不好理解,但是在刷题的过程中经常看到大佬们几行代码就觉得了复杂的算法题,举几个例子:
例子一 LeetCode—617--合并二叉树
找一道题来套解题模板:
1、终止条件:什么情况下递归结束?合并两颗二叉树,我们想到如果其中有一棵树为空的时候,是不是就可以结束递归呢?
2、递归的内容?要合并两颗二叉树,如果像我那样想每次都知道总的结果,那么就会陷入误区。正确的思路应该是在每次递归的时候合并两个子树的root、root.left、root.right,前提是他们不为空。
3、返回值:需要返回什么?根据题目要求的合并两颗二叉树,因此我们需要在每次递归中获得当前递归中的节点信息,因此返回的应该是当前节点合并后的节点。
每次只关心当前递归的数据,不要考虑循环嵌套,Java代码实现如下:
public TreeNode mergeTrees(TreeNode t1,TreeNode t2) {
if(t1==null&&t2==null) return null;
if(t1==null) return t2;
if(t2==null) return t1;
TreeNode root = new TreeNode(t1.val + t2.val); //每次递归创建一个新的节点
root.left = mergeTrees(t1.left,t2.left); //新的节点是由t1和t2组成。如果有一个为空则会返回上面的 t1或者t2
root.right = mergeTrees(t1.right,t2.right);
return root;
}
例子二 LeetCode-226-二叉树的反转
这个问题可以从LeetCode上看到,挺有趣的,同样可以用上面的三个步骤来解决:
1、终止条件:什么情况终止递归?由于是反转二叉树,前提条件肯定是二叉树已经反转完毕,此时终止
2、递归的内容?递归是为了实现什么了?题目的意思是要实现交换二叉树的左右节点,因此能够想到的是将二叉树的左节点放到右节点,右节点放到左节点,根节点不变。
3、返回的内容?只考虑当前这次递归所执行的任务,就是返回 交换顺序的节点,不去考虑其他已经处理或者未处理的节点。
Java代码如下:
private TreeNode invertTree(TreeNode root) {
if(root ==null) return null;
TreeNode left = root.left; 后面的操作会改变 left 指针,因此先保存下来
root.left = invertTree(root.right);
root.right = invertTree(left);
return root;
}
例子3-LeetCode-110-平衡二叉树
根据解题方案解决本题
1、终止条件:在寻找平衡二叉树的时候,首先知道什么是平衡二叉树:它的左子树是平衡二叉树,右子树是平衡二叉树,同时左右子树的节点高度差不大于1。因此终止条件就是子树为空或满足上面的三个条件。
2、递归的内容:递归要实现什么?由于题目是想判断是否是平衡二叉树,因此需要判断当前节点的左右子树是否是平衡二叉树,如果不是返回false;同时需要判断左右子树的高度之差是否<=1,如果不满足则返回false。满足上述条件后则可以判定为true。因此需要在返回的过程中添加高度和状态信息。
3、返回的内容:递归需要返回什么,这个题目返回的内容好像挺多的,因为我们从前面直到解决这道题需要知道高度、一个Boolean量的状态、在处理当前节点的 时候知道的是root、root.left、root.right,因此为了快速的解决可能需要一个全局变量height或者result来记录总的状态。
有了上面的分析,我们来看看Java代码实现
private boolean result = false;
public boolean isBalanced(TreeNode root){
if(root==null) return true;
maxDepth(root);
return result;
}
public int maxDepth(TreeNode root){
if(root==null) return 0;
int left = maxDepth(root.left); //求左子树高度
int right = maxDepth(root.right); //求右子树高度
result = Math.abs(left-right)<=1; //高度小于等于1
return 1+Math.max(left,right);
}
例子4:LeetCode-101-对称二叉树
利用上面的3个步骤分析:
1、递归的终止条件:由于是要求二叉树对称因此只要出现不对称以及遍历完整个树结束。不对称的情况分为以下几种:在最高的节点:root.left ==null || root.right==null 其中有一个不为空,这种情况肯定不对称,另外一种是root左右子树都有但是,root.left.val!=root.right.val;另外就是root==null 也会终止递归,因此可以得出递归终止条件:
2、递归需要做什么:从给出的范例可以看出递归在后续节点需要的判断的是兄弟节点的左右子树的大小,因此具有一定的对称性,这个时候就需要在每一层中找出需要比较的兄弟节点,分为4种情况,兄弟节点必须要满足都对称才行。
3、递归返回的什么:在这个例子中递归需要返回的是二叉树是否对称这个boolean量,因此根据上面的判断条件可写出:
public boolean isSymmetric(TreeNode root) {
if(root==null) return true; //空二叉树
if(root.left==null&&root.right==null) return true; //左右子树都为空
if(root.left==null||root.right==null) return false; //有一个不为空
return subSymmetric(root.left,root.right);
}
public boolean subSymmetric(TreeNode left,TreeNode right){
if(left==null&&right==null) return true; //左右子树都为空
if(left==null||right==null) return false; //有一个不为空
if(left.val!=right.val) return false; //虽然非空 但是左右子树不相等
//左右子树同时满足对称性要求,才能返回true 左右子树位置需要交换对比
return subSymmetric(left.right,right.left)&&subSymmetric(left.left,right.right);
}