1. 递归法
1.1 前序遍历
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList();
preOrder(root, list);
return list;
}
public void preOrder(TreeNode root, List list){
if (root == null) {
return;
}
list.add(root.val);
preOrder(root.left, list);
preOrder(root.right, list);
}
}
1.2 中序遍历
// 递归解决 中序遍历:左中右
class Solution {
public List<Integer> innerorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList();
innerOrder(root, list);
return list;
}
public void innerOrder(TreeNode root, List list) {
if (root == null) {
return;
}
// 左中右
postOrder(root.left, list);
list.add(root.val);
postOrder(root.right, list);
}
}
1.3 后序遍历
// 递归解决 中序遍历:左中右
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList();
postOrder(root, list);
return list;
}
public void postOrder(TreeNode root, List list) {
if (root == null) {
return;
}
// 左右中
postOrder(root.left, list);
postOrder(root.right, list);
list.add(root.val);
}
}
2. 迭代法
1.1 前序遍历
根 左 右
// 迭代法
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
// 创建list存储返回序列;创建stack用于前序遍历
List<Integer> res = new ArrayList();
Deque<TreeNode> stack = new LinkedList();
TreeNode node = root;
while(node != null || !stack.isEmpty()) {
// 因为先根,所以一直向左子树深入,根压栈,根加入res
while(node != null) {
stack.push(node);
res.add(node.val);
node = node.left;
}
// 没有左子树了,出栈判断右子树
node = stack.pop();
node = node.right;
}
return res;
}
}
1.2 中序遍历
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>(); // 创建一个列表用于存储中序遍历结果
Deque<TreeNode> stk = new LinkedList<TreeNode>(); // 创建一个双端队列(栈)用于辅助遍历
while (root != null || !stk.isEmpty()) {
// 遍历左子树并将节点入栈
while (root != null) {
stk.push(root);
root = root.left;
}
// 当左子树遍历完后,弹出栈顶节点,将其值加入res列表
root = stk.pop();
res.add(root.val);
// 遍历右子树
root = root.right;
}
return res; // 返回中序遍历结果列表
}
}
给定的代码使用了一个辅助栈来实现二叉树的中序遍历算法。以下是对代码的详细解释:
- inorderTraversal 方法接受一个名为 root 的 TreeNode 输入,并返回一个包含中序遍历结果的 List。
- 创建一个列表 res 用于存储中序遍历的结果。
- 创建一个双端队列(栈) stk 用于辅助遍历。
- 进入 while 循环,条件是当 root 不为空或者 stk 不为空时,即还有节点需要遍历。
- 在循环中,首先遍历左子树并将节点入栈,这一步将沿着树的左子树路径向下移动,并将经过的节点压入栈中。 当左子树遍历完毕时,执行下面的操作。
- 弹出栈顶节点,并将其值加入结果列表 res。 接着,将 root 指向当前节点的右子树,以便进行右子树的遍历。
- 循环继续,直到所有节点都被遍历。 最后,返回包含中序遍历结果的 res 列表。
该算法利用了栈的先进后出特性,通过遍历左子树并将节点压入栈中,然后弹出栈顶节点进行处理,再遍历右子树。通过不断地在左子树和右子树之间切换,最终完成了中序遍历。
1.3 后序遍历
class Solution {
// 迭代实现二叉树的后序遍历
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode prev = null; // 用于判断当前根节点的右子树是否被完全遍历
while(root != null || !stack.isEmpty()){
while(root != null){
stack.push(root);
root = root.left;
}
root = stack.pop();
// 判断当前根节点的右子树是否被完全遍历
if(root.right == null || root.right == prev){
result.add(root.val); // 当右子树被完全遍历后,将当前根节点的值添加到结果列表中
prev = root; // 更新 prev 为当前根节点
root = null; // 将 root 置为空,以便向上回溯
}
else {
stack.push(root); // 将当前根节点重新入栈
root = root.right; // 遍历右子树
}
}
return result; // 返回后序遍历结果列表
}
}
其中,prev 变量用于帮助判断当前根节点的右子树是否被完全遍历过。
postorderTraversal 方法接受一个名为 root 的 TreeNode 输入,并返回一个包含后序遍历结果的 List。
创建一个列表 result 用于存储后序遍历的结果。
创建一个栈 stack 用于辅助遍历。
创建一个变量 prev,用于判断当前根节点的右子树是否被完全遍历过。
进入 while 循环,条件是当 root 不为空或者 stack 不为空时,即还有节点需要遍历。
在循环中,首先遍历左子树并将节点入栈,这一步将沿着树的左子树路径向下移动,并将经过的节点压入栈中。
当左子树遍历完毕时,执行下面的操作。
弹出栈顶节点,并将其赋值给 root。
使用条件判断语句,判断当前根节点的右子树是否被完全遍历过。如果满足以下条件之一,表示当前根节点的右子树已经被完全遍历过:
- 右子树为空:root.right == null
- 右子树是上一个遍历的节点(prev):root.right == prev
如果满足上述条件,将当前根节点的值加入结果列表 result,并更新 prev 为当前根节点。
将 root 置为空,以便在下一次循环中向上回溯。
如果当前根节点的右子树未被完全遍历,将当前根节点重新入栈,然后将 root 指向当前节点的右子树,以便进行右子树的遍历。
循环继续,直到所有节点都被遍历。
最后,返回包含后序遍历结果的 result 列表。
prev 变量的作用是为了判断当前根节点的右子树是否被完全遍历过。在后序遍历中,我们需要先遍历左子树、再遍历右子树,最后才访问根节点。当右子树被完全遍历后,我们才能将根节点加入到结果列表中。通过使用 prev 变量,我们可以判断当前根节点的右子树是否已经被遍历完,如果是,则可以将根节点加入结果列表中。然后,我们将 prev 更新为当前根节点,继续向上回溯。这样可以确保在遍历完右子树后,才将根节点添加到结果列表中,符合后序遍历的要求。
重新入栈,prev更新,node设为null 是关键