二叉树遍历的统一化写法
二叉树遍历的递归版代码写法很简单,只用简单地调整访问节点操作和递归遍历左右子树的顺序即可写出三种遍历算法的代码。但在面试中,面试官想看的往往是迭代版的代码。然而二叉树的迭代遍历写法真的是五花八门,但原理却又都一样,都是借助栈的后进先出特性来保存遍历过程中的节点信息。下面为大家献上二叉树前、中、后序遍历的统一化迭代写法。
0.前序遍历
算法思路来源于《数据解构(C++语言版)》P127 代码5.14。
public List<int> PreorderTraversal(TreeNode root) {
var stack = new Stack<TreeNode>();
var res = new List<int>();
while (true) {
while (root != null) {
res.Add(root.val);
stack.Push(root.right);
root = root.left;
}
if (stack.Count == 0) break;
root = stack.Pop();
}
return res;
}
先一直沿着树向左搜索,每访问到一个节点,就把该节点添加到的访问列表中,同时记录该节点的右孩子信息。这样当搜索到最左下叶子节点后,即可从栈中弹出该叶子节点父节点的右孩子,进而实现“根-左-右”的前序遍历。
1.中序遍历
算法思路来源于《数据解构(C++语言版)》P129 代码5.15。
public List<int> InorderTraversal(TreeNode root) {
var res = new List<int>();
var stack = new Stack<TreeNode>();
while (true) {
while (root != null) {
stack.Push(root);
root = root.left;
}
if (stack.Count == 0) break;
root = stack.Pop();
res.Add(root.val);
root = root.right;
}
return res;
}
先一直沿着树向左搜索,每访问到一个节点,就记录该节点的信息。这样当搜索到最左下叶子节点,即可从栈中弹出该叶子节点并添加到访问列表中,再弹出该节点的父节点并添加到访问列表中,然后转而搜索该父节点的右孩子,进而实现“根-左-右”的前序遍历。
2.后序遍历
算法思路来源于LeetCode145. 二叉树的后序遍历的题解,主要思想是先实现一个“根-右-左”顺序的前序遍历,然后再将结果翻转即可。
public List<int> PostorderTraversal(TreeNode root) {
var stack = new Stack<TreeNode>();
var res = new List<int>();
while (true) {
while (root != null) {
res.Add(root.val);
stack.Push(root.left);
root = root.right;
}
if (stack.Count == 0) break;
root = stack.Pop();
}
res.Reverse();
return res;
}
和前序遍历一样,只不过我们按照“根-右-左”的顺序来控制入栈和搜索。
3.总结
在刷题的过程中,看到过各种各样的二叉树遍历写法,要么分支控制太多、栈操作太多,要么三种写法不够统一,记忆起来比较苦恼。直到最近在看数据结构的书,被书上的二叉树迭代版遍历算法的简练所折服。又联系到之前掌握的前序、后序的统一化写法,便有了现在的非常简洁的二叉树前、中、后序遍历的统一化写法。