早上有朋友说最近在研究一个遍历,怎么这么眼熟?当时力扣有一道说,有兴趣的可以了解一下,当时成功引起了我的兴趣——morris遍历。
特点是:morris遍历是二叉树遍历算法的超强进阶算法,morris遍历可以将非递归遍历中的空间复杂度降为O(1)。从而实现时间复杂度为O(N),而空间复杂度为O(1)的精妙算法。
morris遍历利用的是树的叶节点左右孩子为空(树的大量空闲指针),实现空间开销的极限缩减。
如果【节点】无左孩子,【节点】向右移动(cur=cur.right)
如果【节点】有左孩子,找到【节点】左子树上最右的节点,记为mostright
如果mostright的right指针指向空,让其指向【节点】,【节点】向左移动(cur=cur.left)
如果mostright的right指针指向【节点】,让其指向空,【节点】向右移动(cur=cur.right)
Morris遍历是使用线索二叉树进行中序遍历的一种实现。
代码网上不少,我这儿放图解
前序遍历
步骤:
-
如果当前节点的左孩子为空,则输出当前节点并将其右孩子作为当前节点。
-
如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点。
a) 如果前驱节点的右孩子为空,将它的右孩子设置为当前节点。输出当前节点(在这里输出,这是与中序遍历唯一一点不同)。当前节点更新为当前节点的左孩子。
b) 如果前驱节点的右孩子为当前节点,将它的右孩子重新设为空。当前节点更新为当前节点的右孩子。
-
重复以上1、2直到当前节点为空。
中序遍历
步骤:
-
如果当前节点的左孩子为空,则输出当前节点并将其右孩子作为当前节点。
-
如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点。
a) 如果前驱节点的右孩子为空,将它的右孩子设置为当前节点。当前节点更新为当前节点的左孩子。
b) 如果前驱节点的右孩子为当前节点,将它的右孩子重新设为空(恢复树的形状)。输出当前节点。当前节点更新为当前节点的右孩子。
-
重复以上1、2直到当前节点为空。
图示:
下图为每一步迭代的结果(从左至右,从上到下),cur代表当前节点,深色节点表示该节点已输出。
后序遍历
后续遍历稍显复杂,需要建立一个临时节点dump,令其左孩子是root。并且还需要一个子过程,就是倒序输出某两个节点之间路径上的各个节点。
步骤:
当前节点设置为临时节点dump。
-
如果当前节点的左孩子为空,则将其右孩子作为当前节点。
-
如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点。
a) 如果前驱节点的右孩子为空,将它的右孩子设置为当前节点。当前节点更新为当前节点的左孩子。
b) 如果前驱节点的右孩子为当前节点,将它的右孩子重新设为空。倒序输出从当前节点的左孩子到该前驱节点这条路径上的所有节点。当前节点更新为当前节点的右孩子。
-
重复以上1、2直到当前节点为空。
图示: