在力扣上做题的时候发现了大佬给的二叉树非递归遍历的模板,前中后序只需要套用一个模板就行了,非常好记。原版本是C++写的,链接如下:
作者:sonp
链接:https://leetcode-cn.com/problems/binary-tree-postorder-traversal/solution/mo-fang-di-gui-zhi-bian-yi-xing-by-sonp/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
以下用java实现,其中的注释也与原版相同。
思路
需要一个标志区分每个递归调用栈,这里使用 nullptr 来表示。
Java代码
先序遍历
访问顺序为中、左、右,入栈顺序为右、左、中。
public List<Integer> preorderTraversal(TreeNode root) {
ArrayList<Integer> output = new ArrayList<>(); //保存结果
LinkedList<TreeNode> stack = new LinkedList<>(); //调用栈
if(root == null) return output;
stack.add(root); //首先入栈root节点
TreeNode temp;
while(!stack.isEmpty()){
temp = stack.pollLast(); //访问过的节点弹出
if(temp != null){
if(temp.right != null) stack.add(temp.right); //右节点先压栈,最后处理
if(temp.left != null) stack.add(temp.left);
stack.add(temp); //当前节点重新压栈(留着以后处理),因为先序遍历所以最后压栈
stack.add(null); //在当前节点之前加入一个空节点表示已经访问过了
}
else{//空节点表示之前已经访问过了,现在需要处理除了递归之外的内容
temp = stack.pollLast(); //temp是null之前压栈的一个节点,也就是上面stack.add(temp)中的那个temp
output.add(temp.val); //处理当前节点
}
}
return output;
}
中序遍历
访问顺序为左、中、右,入栈顺序为右、中、左。
只有注释的地方的顺序改了,哈哈是不是很好记,为大佬点赞。
public List<Integer> inorderTraversal(TreeNode root) {
ArrayList<Integer> output = new ArrayList<>();
LinkedList<TreeNode> stack = new LinkedList<>();
if(root == null) return output;
stack.add(root);
TreeNode temp;
while(!stack.isEmpty()){
temp = stack.pollLast();
if(temp != null){
if(temp.right != null) stack.add(temp.right);
stack.add(temp); //在左节点之前重新插入该节点,以便在左节点之后处理(访问值)
stack.add(null); null跟随temp插入,标识已经访问过,还没有被处理
if(temp.left != null) stack.add(temp.left);
}
else{
temp = stack.pollLast();
output.add(temp.val);
}
}
return output;
}
后序遍历
访问顺序为左、右、中,入栈顺序为中、左、右。
public List<Integer> postorderTraversal(TreeNode root) {
ArrayList<Integer> output = new ArrayList<>();
LinkedList<TreeNode> stack = new LinkedList<>();
if(root == null) return output;
stack.add(root);
TreeNode temp;
while(!stack.isEmpty()){
temp = stack.pollLast();
if(temp != null){
stack.add(temp); //在右节点之前重新插入该节点,以便在最后处理(访问值)
stack.add(null); //nullptr跟随t插入,标识已经访问过,还没有被处理
if(temp.right != null) stack.add(temp.right);
if(temp.left != null) stack.add(temp.left);
}
else{
temp = stack.pollLast();
output.add(temp.val);
}
}
return output;
}
对比中序遍历的递归写法
void dfs(t){ //进入函数表示“访问过”,将t从栈中弹出
dfs(t->left); //因为要访问t->left, 所以我先把函数中下面的信息都存到栈里。
//依次call.push(t->right), call.push(t)【t第二次入栈】, call.push(nullptr)【标识t二次入栈】, call.push(t->left)。
//此时t并没有被处理(卖萌)。栈顶是t->left, 所以现在进入t->left的递归中。
//res.push_back(t->val)
t.卖萌(); //t->left 处理完了,t->left被彻底弹出栈。
//此时栈顶是nullptr, 表示t是已经访问过的。那么我现在需要真正的处理t了(即,执行卖萌操作)。
//卖萌结束后,t 就被彻底弹出栈了。
dfs(t->right);
}