几种遍历二叉树的方法
遍历二叉树是学习算法与数据结构必学的内容,在leetcode网站上有好几道相关的题,本文也是从那里引出的:
- Binary Tree Preorder Traversal
- Binary Tree Inorder Traversal
- Binary Tree Postorder Traversal
- Binary Tree Level Order Traversal
- Binary Tree Level Order Traversal II
- Binary Tree Zigzag Level Order Traversal
以上就是leetcode上相关的题目。二叉树的先序、中序和后序遍历(对应上面的1,2,3)的递归实现非常简单。如下:
1 | void preorder(TreeNode *root) |
简单明了。但它们的迭代实现却要写上好几倍的代码。
下面根据leetcode的那几道题说说几种遍历方法的迭代实现。
先序遍历的迭代实现
从其递归实现中,可以清晰的找到迭代实现的思路:访问跟节点,再访问左子结点,访问完左子结点后再访问右子结点。因此,我们需要一个栈来保存右子结点。其实现比较简单,如下:
1 | class Solution { |
中序遍历的迭代实现
中序遍历的迭代实现和先序的有些类似。1. 先将所有左子结点(包括最开始的根节点)入栈,2. 访问栈顶元素,3. 然后又将栈顶元素的右子结点入栈,重复1。
1 | class Solution { |
后序遍历的迭代实现
后序遍历的迭代实现有点麻烦。这里需要为每个节点添加一个标记位,用于标识访问了左子结点还是右子结点,且每个节点需要入栈两次。我用个例子说明一下吧。
假设我们有如下的一颗二叉树:
1 | # tree |
其后序遍历的结果应为4526731
。我们用一个栈保存添加了标志位的节点。依旧是所有左节点都需要入栈。先将124
入栈:
1 | [<1, L>, <2, L>, <4, L>] |
这个时候左子结点已经为NULL了,我们需要读栈了,将栈顶pop出来,即<4, L>
。我们发现其标志位为L
,现在将它置为R
,并重新入栈,同时拿到它的右子结点,4
的右子结点为NULL
,所以这个时候再次读栈顶,得<4, R>
,由于标志位为R
,所以访问该节点。读下一个栈顶元素<2, L>
,置标志位为R
,重新入栈,得到其右子结点5
,将该节点入栈<5, L>
,将节点5
得所有左子结点入栈。
重复做这种置标志位,入栈,重置标志位,入栈,当标识位位R
时访问得操作,最终后序遍历完整棵树。
例子的栈的变化如下
1 | [<1, L>] |
实现如下:
1 | class Solution { |
按层遍历
开头列的几道leetcode的题,后面三道都属于按层遍历,解法一样,我用一个队列用于存放要访问的节点,和两个数(一个记录当前层需要访问多少个节点,一个记录下一层需要访问的节点数)来解决。
Binary Tree Level Order Traversal,我的解法如下:
1 | class Solution { |
Binary Tree Level Order Traversal II,只需要将Binary Tree Level Order Traversal的结果在返回前将其反转就行了。
1 | // add before return statement |
Binary Tree Zigzag Level Order Traversal,则是将Binary Tree Level Order Traversal的结果在返回前隔层反转就行了。
1 | // add before return statement |