本文是递归算法系列文的第7篇,依然沿着递归的脉络,介绍了常常运用递归处理问题的一个典型数据结构——二叉树。分析总结了LeetCode中的相关习题在解法上的思路和模板。
本文内容如下:
- 树的前、中、后序、层次遍历的递归和非递归写法
- LeetCode上树的问题分类(基本遍历、路径、计数、加和、深宽、构造、BST等)
- 两种遍历为思想的问题(
判定、比较结点或子树
以及路径[ 和 ]、累计
) - 【小结】在解决树的遍历相关问题时,我们是如何使用基本遍历方法,进行递归设计的?
由于树的相关问题题目较多,本文介绍第一部分,其余部分后续更新。(又挖了一个坑)
0.LeetCode中二叉树定义
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) {
val = x; }
}
1.四种遍历(递归+非递归)
1.1递归遍历
List<Integer> res = new ArrayList<>();
public List<Integer> preorderTraversal(TreeNode root) {
if(root == null) return res;
res.add(root.val);
preorderTraversal(root.left);
preorderTraversal(root.right);
return res;
}
List<Integer> res = new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root) {
if(root == null) return res;
inorderTraversal(root.left);
res.add(root.val);
inorderTraversal(root.right);
return res;
}
List<Integer> res = new ArrayList<>();
public List<Integer> postorderTraversal(TreeNode root) {
if(root == null) return res;
postorderTraversal(root.left);
postorderTraversal(root.right);
res.add(root.val);
return res;
}
1.2非递归遍历
前序
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null) return res;
Deque<TreeNode> stack = new ArrayDeque<>();
TreeNode p = root;
while(!stack.isEmpty() || p != null){
while(p!= null){
//一口气走到左下最后一个,一边走一边入栈、同时加入结果集
stack.push(p);
res.add(p.val);
p = p.left;
}
p = stack.pop().right; //逐个往上、然后遍历右子树
}
return res;
}
注:前序还有一种写法:这种写法具有结构对称美哦~后序就知道了
- 根不空时,根入栈
- 当栈非空时:
- 根出栈,加入res。
- 若右子树非空,右子树入栈
- 若左子树非空,左子树入栈
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null) return res;
Deque<TreeNode> stack = new ArrayDeque<>();
TreeNode p = root;
stack.push(p); //根节点入栈
while(!stack.isEmpty()){
//当根节点不空时,出栈一个根节点,然后加入res中
p = stack.pop();
res.add(p.val);
if(p.right != null) //加入右子树入栈
stack.push(p.right);
if(p.left != null) //左子树入栈
stack.push(p.left);
}
return res;
}
中序
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null) return res;
Deque<TreeNode> stack = new ArrayDeque<>();
TreeNode p = root;
while(!stack.isEmpty() || p != null){
//一口气走到左下角一边走,一边入栈,
while(p != null){
//但是是在出栈后才加到结果集
stack.push(p);
p = p.left;
}
p = stack.pop();
res.add(p.val);
p = p.right;
}
return res;
}
后序
后序遍历使用了一个小技巧:
- 后序遍历是右 =》左 =》 根, 那么我们可以先按照根 =》左 =》右(前序)进行遍历,然后将得到的结果进行翻转
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null) return res;
Deque<TreeNode> stack = new ArrayDeque<>();
// Deque<TreeNode> temp_res = new ArrayDeque<>();
TreeNode p = root;
stack.push(p);
while(!stack.isEmpty()){
p = stack.pop();
res.add(p.val);
if(p.left != null)
stack.push(p.left);
if(p.right != null)
stack.push(p.right);
}
Collections.reverse(res); //将结果翻转,这一步也可以用栈
return res;
}
1.3层次遍历
以LC102为例:输入二叉树数组,输出层次遍历结果,并且每一层为一个list,整体为二维list
https://leetcode-cn.com/problems/binary-tree-level-order-traversal/
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if(root == null)