树的遍历无论是在考试还是面试中都是一大热点,大多数学生都对普通的递归法中序,前序,后序遍历,以及广度优先法的层次遍历非常熟悉,但不难发现上述方法的空间复杂度其实都是O(n)的(递归会占用内部堆栈),今天就让我们来认识一种全新的迭代的遍历方法 -- Morris Traversal。
使用 Morris Traversal,我们可以在不使用堆栈和递归的情况下遍历树。Morris Traversal 的思想是基于Threaded Binary Tree的。在此遍历中,我们首先创建指向中序后继树的链接并使用这些链接打印数据,最后还原更改以恢复原始树。
具体操作如下(中序)
1. 将current初始化为root
2. 当current不为Null时,做一下操作
如果current的左子为Null则:
a) 打印current的值
b) 更新current为current的右子,i.e. current = current => current.right
否则
a) 寻找current的左子树上的最右的节点(后继节点)或以current作为右节点的 节点,设这个节点为n
I) 如果n的右子节点为current
1)更新n的右节点为Null
2)打印current的值
3)更新current为current的右子,i.e. current = current => current.right
II)如果n的右节点为Null
1)更新n的右子节点为current
2)更新current为current的左子,i.e. current = current => current.left
虽然我们在遍历中修改了树,但完成后又恢复到原来的形状。与基于堆栈的遍历不同,此遍历不需要额外的空间。
前序遍历与中序遍历类似
1. 如果左子树为Null,则打印current的数据,并移动到右子。
2. 否则,使current的前驱节点右子节点指向当前节点,并可能出现两种情况:
a)前序节点的右子节点已经指向当前节点。将其右子设置为 NULL,更新current为current的右子
b)前驱节点右子为 NULL,将其右子节点指向current,打印current的数据并移动到current的左子节点。
3. 迭代重复上述操作直到current为Null
后序遍历的操作与前序遍历操作对称,只需向右迭代即可
时间复杂度: O(n)
如果我们仔细观察,我们可以注意到树的每条边最多被遍历 3 次。在最坏的情况下,创建和删除相同数量的额外边(作为输入树)。
空间复杂度:O(1)
因为只使用常量变量
完整代码实现下载链接:
(包含各种语言:C语言、Python、Java、C++、C#、Javascript等均有示例)
免费资源下载:Morris Traversal