树的遍历
1、 前序遍历
一、给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
示例 1:
输入:root = [1,null,2,3]
输出:[1,2,3]
提示:
树中节点数目在范围 [0, 100] 内
-100 <= Node.val <= 100
树的定义:
public 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;
}
}
(1)递归
public class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List res = new ArrayList();
helper(root, res);
return res;
}
public void helper(TreeNode root, List res){
if (root == null){
return;
}
res.add(root.val);
helper(root.left, res);
helper(root.right, res);
}
}
(2)迭代
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
LinkedList<TreeNode> stk = new LinkedList<TreeNode>();
while (root != null || !stk.isEmpty()) {
if (root != null) {
res.add(root.val);
stk.addFirst(root);
root = root.left;
}else {
root = stk.removeFirst();
root = root.right;
}
}
return res;
}
}
2、中序遍历
给定一个二叉树的根节点 root ,返回它的 中序 遍历。
示例1:
输入:root = [1,null,2,3]
输出:[1,3,2]
提示: 树中节点数目在范围 [0, 100] 内
-100 <= Node.val <= 100
树的定义:
public 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;
}
}
(1)递归
使用递归方法,创建辅助函数helper,使用尾递归。
递归操作:
定义 helper(root) 表示当前遍历到 root 节点的答案。按照定义,我们只要首先将 root 节点的值加入答案,然后递归调用 helper(root.left) 来遍历 root 节点的左子树,最后递归调用 helper(root.right) 来遍历 root 节点的右子树即可。
递归终止的条件:
碰到空节点。
public class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List res = new ArrayList();
helper(root, res);
return res;
}
public void helper(TreeNode root, List res){
if (root == null){
return;
}
helper(root.left, res);
res.add(root.val);
helper(root.right, res);
}
}
复杂度分析
时间复杂度:O(n)O(n),其中 nn 为二叉树节点的个数。二叉树的遍历中每个节点会被访问一次且只会被访问一次。
空间复杂度:O(n)O(n)。空间复杂度取决于递归的栈深度,而栈深度在二叉树为一条链的情况下会达到 O(n)O(n) 的级别。
(2)迭代
区别:递归的时候隐式地维护了一个栈,而我们在迭代的时候需要显式地将这个栈模拟出来
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
LinkedList<TreeNode> stk = new LinkedList<TreeNode>();
while (root != null || !stk.isEmpty()) {
if (root != null) {
stk.addFirst(root);
root = root.left;
}else {
root = stk.removeFirst();
res.add(root.val);
root = root.right;
}
}
return res;
}
}
逐步搜索根节点的左子节点,入栈,当无左子节点时res列表中加入当前节点的值,将当前节点的右子节点入栈,无右子节点则继续出栈。
复杂度分析
时间复杂度:O(n)O(n),其中 nn 为二叉树节点的个数。二叉树的遍历中每个节点会被访问一次且只会被访问一次。
空间复杂度:O(n)O(n)。空间复杂度取决于栈深度,而栈深度在二叉树为一条链的情况下会达到 O(n)O(n) 的级别。
3、 后序遍历
给定一个二叉树,返回它的 后序 遍历。
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [3,2,1]
树的定义:
public 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;
}
}
(1)递归
public class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List res = new ArrayList();
helper(root, res);
return res;
}
public void helper(TreeNode root, List res){
if (root == null){
return;
}
helper(root.left, res);
helper(root.right, res);
res.add(root.val);
}
}
(2)迭代
public class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List res = new ArrayList();
LinkList<TreeNode> stack = new ArrayList<>();
TreeNode prev = root;
while(root != null || !stack.isEmpty()){
while (root != null) {
stack.push(root);
root = root.left;
}
root = stack.pop();
// 如果右子树为空或者右子树已被遍历
if (root.right == null || root.right == prev) {
res.add(root.val);
prev = root;
root = null;
} else {
stack.push(root);
root = root.right;
}
}
return res;
}
}
复杂度分析
-
时间复杂度:O(n),其中 n 是二叉搜索树的节点数。每一个节点恰好被遍历一次。
-
空间复杂度:O(n),为迭代过程中显式栈的开销,平均情况下为 Ologn ,最坏情况下树呈现链状,为 O(n)。