大部分笔记都写在印象笔记上,对于有意义的学习,会整理出来分享。
通过刷题来积累经验,如果错误,还望各位前辈指正。
一. 230题 二叉树中第K小的元素,对递归有了一点理解
首先,递归理解为,递下去和归上来。要注意递下去的终止条件和归上来的开始条件是一个条件。但是我们递归的时候,不仅仅是为了遍历,而是想要每层做一些事情,一般我们都是在归上来的时候添加上我们想要做的事情。
其次,我们的思维要保持清晰,其实递下去是很简单的,只需要再次调用该函数即可,只不过改变下参数而已,例如这里的参数可能是树的子树的根节点。所以重点在于递下去的终止条件(归上来的开始条件)和每层归上来要进行的额外操作。
最后,我现在认为,一开始写递归函数时,应该现在函数体的开始写递下去的终止条件(即归上来的开始条件),然后写递下去(即调用此递归方法),最后写每层我们想要做的事情。
补充:以上是针对递归函数没有返回值的情况。如果有返回值,则可以将返回值理解为我们想要做的事情的结果返回,也是作为我们想要做的事情的一部分。
举例:
1. 230. 二叉搜索树中第K小的元素 给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。
注:此方法不是最优,只是想要通过代码来表示我的理解:有三部分组成,①递下去的终止条件(即归上来的开始条件);②递下去;③我们想要做的事情
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 {
List<Integer> orderedList = new ArrayList<Integer>();
public int kthSmallest(TreeNode root, int k) {
findNode(root);
orderedList.sort(new Comparator<Integer>() {//使用匿名Comparator类,并重写compare方法
@Override
public int compare(Integer o1, Integer o2) {//当满足compare的返回值条件时,就进行o1和o2的位置交换
return Integer.compare(o1, o2);
}
});
return orderedList.get(k-1);
}
public void findNode(TreeNode root) {
//递下去的终止条件(即归上来的开始条件)
if(root == null) {
return;
}
//递下去
findNode(root.left);
findNode(root.right);
//想要做的
orderedList.add(root.val);
}
}
2. 经典递归例子,前n项积
public class LeetCodeTest {
public static void main(String[] args) {
int mul = LeetCodeTest.mul(4);
System.out.println(mul);
}
public static int mul(int val) {
//递下去的终止条件(归上来的起始条件)
if(val == 1) {
return val;
}
//递下去
int mul = mul(val - 1);
//我们想要做的
return mul * val;
}
}
二. 94题 递归法中序遍历----递归结构没有那么明显
使用递归法进行中序遍历: 力扣
①添加当前结点的前驱结点;②添加当前结点;③添加当前结点的后继结点
private void inorderTraversal(TreeNode root, List<Integer> arr) {
if(root == null) {//递下去的终止条件(归上去的开始条件)
return;
}
inorderTraversal(root.left, arr);//递下去
arr.add(root.val);//额外的操作--按照中序遍历添加 每个 结点
inorderTraversal(root.right, arr);//递下去
}
感觉合情合理,但是又感觉相互纠缠 。纠缠表现在:有两次调用递归方法,每一个递下去的时候都又会调用另一个。
纠缠是外表,递下去也是外表,内因在于我们想要做的什么?我们想要按照中序遍历的方式添加值。重点在于 遍历结点的顺序 和 添加 ==> 按照正确的结点顺序添加值。
很自然的(在中序遍历要求下我们要去找到第一个遍历的结点),我们要去:
inorderTraversal(root.left, arr);//递下去
从而,递下去的终止条件(归上来的起始条件):
if(root == null) {//递下去的终止条件(归上去的起始条件)
return;
}
又是很自然的,当我们第一次归上来,即找到中序遍历的第一个结点,我们肯定要保存/记录下来:
arr.add(root.val);//额外的操作--按照中序遍历添加 每个 结点
最后,当我们第一次归上来后,得到了中序遍历的第一个结点,我们又该去找第一个结点的后继结点,又是很自然的写出下一句:
inorderTraversal(root.right, arr);//递下去
另外需要补充的是,我们在上面的代码中,想要去找后继结点,可能是想要用到不断将root.right去left找到后继节点;也有可能是要判断root.right是不是null。
之后便不断地重复归上去了。
所以从第一个值开始,从归上去(方向是从递归栈的栈顶向下---栈顶先出栈)的方向考虑①②③。
理解有误请指正,谢谢。