根节点
在查询中的位置
根
–>左–>右根
–>右根
–>中递归方式解决二叉树问题最重要的是先知道当前根节点需要做什么,然后再根据函数定义进行递归调用子节点,如何提炼出叶子结点要做的事情也正是二叉树的难点所在。
// 递归法
class Solution {
// 前序遍历
public void preorder(TreeNode root, List<Integer> result) {
if (root == null) {
return;
}
result.add(root.val);
inorder(root.left, result);
inorder(root.right, result);
}
// 中序遍历
public void inorder(TreeNode root, List<Integer> result) {
if (root == null) {
return;
}
inorder(root.left, result);
result.add(root.val);
inorder(root.right, result);
}
// 后序遍历
public void postorder(TreeNode root, List<Integer> result) {
if (root == null) {
return;
}
inorder(root.left, result);
inorder(root.right, result);
result.add(root.val);
}
public List<Integer> traversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
inorder(root, result);
return result;
}
}
代码使用Java语言通过递归方法实现二叉树的前序、中序和后序遍历:
前序遍历(Preorder Traversal):
中序遍历(Inorder Traversal):
后序遍历(Postorder Traversal):
可以发现在递归实现不同顺序的遍历时,调整的只是对跟节点的访问顺序。
后序遍历的顺序是根节点、左子树、右子树。
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) {
this.val = val;
this.left = null;
this.right = null;
}
}
public class PreorderTraversalIterative {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) {
return result;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
result.add(node.val); // 将节点值添加到结果列表中
// 注意:因为栈是先进后出的结构,所以先将右子节点压栈,再将左子节点压栈
if (node.right != null) {
stack.push(node.right);
}
if (node.left != null) {
stack.push(node.left);
}
}
return result;
}
public static void main(String[] args) {
// 创建一个二叉树
TreeNode root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);
PreorderTraversalIterative traversal = new PreorderTraversalIterative();
List<Integer> result = traversal.preorderTraversal(root);
System.out.println(result);
}
}
后序遍历的顺序是左子树、右子树、根节点。
可通过对前序遍历的代码稍加改动来实现后序遍历:
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) {
this.val = val;
this.left = null;
this.right = null;
}
}
public class PostorderTraversalIterative {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) {
return result;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
// 在结果列表的最前面插入节点值,以保证后序遍历的顺序
result.add(0, node.val);
// 注意:因为栈是先进后出的结构,所以先将左子节点压栈,再将右子节点压栈
if (node.left != null) {
stack.push(node.left);
}
if (node.right != null) {
stack.push(node.right);
}
}
return result;
}
public static void main(String[] args) {
// 创建一个二叉树
TreeNode root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);
PostorderTraversalIterative traversal = new PostorderTraversalIterative();
List<Integer> result = traversal.postorderTraversal(root);
System.out.println(result);
}
}
在后序遍历中,需要在结果列表的最前面插入节点的值,这样可以保证后序遍历的顺序。在迭代过程中,先将左子节点压入栈,再将右子节点压入栈,然后在结果列表的最前面插入根节点的值。
因为中序遍历需要先访问左子树,然后访问根节点,最后访问右子树,所以中序遍历的迭代实现稍微有些不同。过程中需要在沿着左子树一直走到底的时候才能访问根节点,因此在使用迭代实现中序遍历时,需要确保在访问完左子树之后才访问根节点。
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) {
this.val = val;
this.left = null;
this.right = null;
}
}
public class InorderTraversalIterative {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) {
return result;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode current = root;
while (current != null || !stack.isEmpty()) {
// 先将左子树的所有节点压栈
while (current != null) {
stack.push(current);
current = current.left;
}
// 当前节点为 null 时,表示已经到达左子树的最左边了,可以弹出节点并访问
current = stack.pop();
result.add(current.val);
// 继续遍历右子树
current = current.right;
}
return result;
}
public static void main(String[] args) {
// 创建一个二叉树
TreeNode root = new TreeNode(1);
root.right = new TreeNode(2);
root.right.left = new TreeNode(3);
InorderTraversalIterative traversal = new InorderTraversalIterative();
List<Integer> result = traversal.inorderTraversal(root);
System.out.println(result);
}
}
在这个实现中,我们利用了一个栈来模拟递归的过程。我们从根节点开始,一直向左走到最底层的左子树,将沿途遇到的节点压入栈中。然后开始弹出栈顶的节点并访问它,然后转向其右子树,重复这个过程,直到栈为空并且当前节点也为空时,遍历结束。
总之,栈在模拟深度优先遍历和维护遍历顺序时更加自然和方便,因此在前、中、后序遍历的迭代实现中常常采用栈来模拟递归。队列则更适合广度优先遍历,比如 坚持刷题 | 二叉树的层序遍历等。