二叉树的迭代遍历
二叉树迭代遍历的思路,下面的代码是中序遍历:
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList();
Stack<TreeNode> stack = new Stack();
// 需要针对当前节点 深入其左子树
TreeNode curr = root;
// 当前节点不为空或者stack不为空
while(curr != null || !stack.isEmpty()) {
// 如果当前节点不为空,需要深入当前节点的左子树
if (curr != null) {
stack.push(curr);
curr = curr.left;
// 否则,证明上一个节点已经没有左子树了
// 这时,上一个节点应该被消费了,并且
// 将它的右子树继续当作一个新的树递归
// 注意,此时栈可能没空,被消费的节点上面可能还有很多节点没有被消费或被扫描
} else {
TreeNode t = stack.pop();
list.add(t.val);
curr = t.right;
}
}
return list;
}
用递归的思路思考#
当迭代一个树时,我们要用递归的方式思考,会简单些。比如迭代树时会出现很多这样的代码:
curr = curr.left;
此时就是将当前节点的左子树作为一颗新的树重复算法中curr
经历过的过程,而不要只是看作“取curr.left赋值给curr”,虽然没错,但少了很多生动。
上面代码中何时curr==null
#
curr是当前正在迭代中的子树,所以下面的代码预示着,当当前子树没有左子树时,下一轮迭代curr
为空:
curr = curr.left;
下面的代码出现在循环中curr==null
的分支里,这代表栈中需要最先被消费的子树t
没有右子树时,下一轮迭代curr
为空,而进入这个分支的前提条件是,子树t
也没有左子树,就是说子树t
已经是叶子节点:
curr = t.right;
所以
- 当当前子树没有左子树时,下轮curr为空
- 当
curr
为null,且栈中需要最先被消费的子树是个叶子节点时,下轮curr为空
而当前子树为null
时,证明左边已经遍历到底了,这时就开启从栈中取最先需要被消费的节点,消费它,并迭代它的右子树的情况。
最初掉进的坑#
最初是想用一个curr
记录当前的节点,从根节点开始,一直向左,每次都将经过的节点入栈,直到没有左节点,代码就是这样:
Stack<TreeNode> stack = new Stack();
TreeNode curr = root;
while(curr != null) {
if (curr.left != null) {
stack.push(curr);
curr = curr.left;
} else {
// ... ???
}
}
然后我在else里应该怎么做?我知道父节点,也就是上一个节点肯定是栈顶节点,那我弹出个节点作为curr?可是再次遍历时它的left
又有了啊
于是就去看了题解,由于是中序遍历,这个时候其实完全可以不让它作为curr
,而是直接消费了这个节点,假设这个节点是A,因为按照中序的原则,A当前正在被遍历,消费之后再把它的右子节点作为一个新的子树入栈,继续重复迭代过程。所以就看到了上面的代码。
前序遍历和后序遍历#
思路完全没有区别,就是消费节点的位置不一样:
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList();
Stack<TreeNode> stack = new Stack();
TreeNode curr = root;
while(curr != null || !stack.isEmpty()) {
if (curr != null) {
stack.push(curr);
// 前序在这里消费节点
list.add(curr.val);
curr = curr.left;
} else {
TreeNode t = stack.pop();
curr = t.right;
}
}
return list;
}
}
后序遍历的代码较难,因为无法确定栈顶节点的右子树是否已经被遍历过了,如果没有遍历过是不能直接消费栈顶节点的。这里使用一个prev
来记录上一个被消费的节点,因为根据后序遍历原则,上一个被消费的节点一定是当前节点的直接右子节点:
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList();
Stack<TreeNode> stack = new Stack();
TreeNode curr = root, prev = null;
while(curr != null || !stack.isEmpty()) {
if (curr != null) {
stack.push(curr);
curr = curr.left;
} else {
TreeNode t = stack.peek();
if (t.right == null || t.right == prev) {
stack.pop();
list.add(t.val);
prev = t;
} else {
curr = t.right;
}
}
}
return list;
}
}
框架#
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList();
Stack<TreeNode> stack = new Stack();
TreeNode curr = root;
while(curr != null || !stack.isEmpty()) {
if (curr != null) {
stack.push(curr);
// ...
} else {
// 按规则操作栈
}
}
return list;
}
作者:Yudoge
出处:https://www.cnblogs.com/lilpig/p/16576730.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
欢迎按协议规定转载,方便的话,发个站内信给我嗷~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· winform 绘制太阳,地球,月球 运作规律
· 上周热点回顾(3.3-3.9)