简介
morris 算法是一种二叉树的遍历算法,其利用树叶子节点的孩子为空的特点,将空的孩子节点临时指向其后继节点,后续再次遍历到该节点时重置为空使其恢复树的结构。这种遍历算法能够压缩空间,使其在 O(1)
的空间复杂度内完成对树结构的遍历。
算法主要过程
从根节点触发,对每个节点执行以下步骤
- 如果存在左孩子,则找出左孩子的最右节点,
- 如果该节点为空,则将其右指针指向当前节点(这是第一次遍历到该节点),将当前指针移到左孩子上
- 如果该节点指向了当前节点,则将其右指针置为空,恢复树的结构(这是第二次遍历到该节点),将当前指针移到右孩子上
- 如果不存在左孩子,则将当前指针移到右孩子上,进行下一轮遍历
通过上述遍历过程,如果某个节点有左孩子,则该节点被访问两次,如果节点没有左孩子,则该节点只被访问一次,因此,上述的遍历过程时间复杂度为 O(n)
。由于使用循环遍历,没有使用递归的方式,因此不会有额外的空间消耗,其空间复杂度为 O(1)
。
下面用一个例子来说明 morris 算法的遍历过程,假设有以下一颗树:
首先,指针在节点1
处,由于有左孩子 2
,所以找到 2
的最右节点 5
,由于节点 5
的右孩子为空,因此将其指向节点 1
,并将当前指针移到左孩子节点 2
上。如下图所示
接着,由于节点 2
有左孩子 4
,所以找到 4
的最右节点是它本身,由于节点 4
的右孩子为空,因此将其指向节点 2
,并将当前指针移到左孩子节点 4
上。如下图所示
接着,节点 4
没有左孩子,直接到右孩子上(上一步骤已经将节点 4
的右孩子指向节点 2
)上
由于节点 2
有左孩子 4
,所以找到 4
的最右节点是它本身,由于节点 4
的右孩子指向了当前节点,所以将节点 4
的右孩子节点置为空(还原树的结构),然后将当前指针移到右节点 5
上
节点 5
没有左孩子,直接到右孩子上(之前步骤已经将节点 5
的右孩子指向节点 1
)上
由于节点 1
有左孩子 2
,所以找到 2
的最右节点 5
,由于节点 5
的右孩子指向了当前节点 1
,所以将节点 5
的右孩子置空(还原树的结构),然后将当前指针移到右节点 3
上
由于节点 3
有左孩子 6
,所以找到 6
的最右节点是它本身,由于节点 6
的右孩子为空,因此将其指向节点 3
,并将当前指针移到左孩子节点 6
上
节点 6
没有左孩子,直接到右孩子上(上一步骤已经将节点 6
的右孩子指向节点 3
)上
由于节点 3
有左孩子 6
,所以找到 6
的最右节点是它本身,由于节点 6
的右孩子指向了当前节点,所以将节点 6
的右孩子节点置为空(还原树的结构),然后将当前指针移到右节点 7
上
由于节点 7
的左右孩子都是空的,因此遍历结束。
前序遍历
使用 morris 算法可以在 O(1)
的空间复杂度和 O(n)
的时间复杂度下完成对树的前序遍历,而使用递归的方式,则需要 O(H)
(树的高度,函数递归栈)的空间复杂度和 O(n)
的时间复杂度。一般情况下,都是使用递归的方式进行前序遍历(简单直观),如下所示
1 | private void preOrderTraversal(TreeNode root) |
而如果对空间复杂度有特殊的要求,则可以使用 morris 算法来完成树的前序遍历,代码如下
1 | private void preOrderMorrisTraversal(TreeNode root) |
中序遍历
递归的中序遍历也很简单直观,代码如下
1 | private void inOrderTraversal(TreeNode root) |
morris 的中序遍历跟前序遍历差不多,唯一不同就是访问根节点的顺序不同,前置遍历将访问根节点放在了第一次找根节点左子树最右节点的时候,中序遍历将访问根节点放在了第二次找根节点左子树最右节点的时候。
1 | private void inOrderMorrisTraversal(TreeNode root) |
后序遍历
递归的后续遍历,仍然是简单直观的
1 | private void postOrderTraversal(TreeNode root) |
但 morris 的后序遍历就稍微复杂了点,其需要在第二次找根节点左孩子的最右节点时,以根节点左孩子到这个最右节点之间构成的链表进行反向遍历,完成树的后续遍历操作。
1 | private void postOrderMorrisTraversal(TreeNode root) |
参考资料
[1] 风之筝.经典算法小评(2)——Morris树遍历算法 [J/OL]. https://ghh3809.github.io/2018/08/06/morris-traversal/#morris%E9%81%8D%E5%8E%86, 2018-08-06
[2] god-jiang. 神级遍历——morris[J/OL]. https://zhuanlan.zhihu.com/p/101321696, 2020-01-06