数据结构与算法(python):二叉树的遍历(Morris)
1、简介
1.1、what?
通常二叉树遍历的实现方法主要有两类:递归实现和迭代实现,具体可以参考:https://blog.csdn.net/weixin_41665360/article/details/88861801#63_87
递归实现代码简单,迭代实现效率更高。这里将介绍第三类二叉树遍历方法: M o r r i s Morris Morris 遍历。
既然已经有两类遍历方法,为什么还要第三类方法呢?这种方法有什么特点呢?
1.2、How?
我们知道,在使用迭代的方法深度优先遍历二叉树时,通常需要栈来存储路径,通常空间复杂度为: O ( H ) O(H) O(H),在树高度特别高的情况下,算法会占用大量的存储空间。 M o r r i s Morris Morris 遍历方法正是为了解决这一问题而生,该算法空间复杂度为:O(1)。
我们都知道,二叉树深度优先遍历,一个重要的思想是:回溯。要想实现回溯,算法必须知道自己走过的路径。那么如何只使用固定大小的空间还能清楚路径呢?
M o r r i s Morris Morris 遍历的构思非常巧妙。通过观察不难发现,每次算法回溯都是在左子树遍历完以后。 M o r r i s Morris Morris 利用左子树最后一个结点,将最后一个结点的 r i g h t right right 设置为根节点,这样在遍历完左子树以后,算法可以顺势转到根节点,实现回溯!
可以发现,算法并非通过避免记录路径来节省遍历空间,而是充分利用树结点未利用的空间 n o d e . r i g h t node.right node.right 来链接到根节点实现回溯。
这样又产生一个问题:为 n o d e . r i g h t node.right node.right 赋值会改变原来树的结构!因此,在回溯访问完结点后,需要把链接删除!
2、Python 实现 Morris 遍历
寻找左子树最右结点
2.1、Morris 先序遍历
def preorder(root):
if not root: return
p = root; prenode = None
while p:
if p.left:
prenode = p.left
while prenode.right and prenode.right != p:
prenode = prenode.right
if not prenode.right: #建立链接方便回溯
print(p.val) #打印
prenode.right = p
p = p.left
continue
if prenode.right == p:
prenode.right = None #回溯完成删除多余链接
if not p.left: print(p.val) #打印
p = p.right
2.2、Morris 中序遍历
def inorder(root):
if not root: return
p = root; prenode = None
while p:
if p.left:
prenode = p.left
while prenode.right and prenode.right != p:
prenode = prenode.right
if not prenode.right: #建立链接方便回溯
prenode.right = p
p = p.left
continue
if prenode.right == p:
print(p.val) #打印
prenode.right = None #回溯完成删除多余链接
if not p.left: print(p.val) #打印
p = p.right
2.3、Morris 后序遍历
def ReverseAndPrint(root):
if not root: return
node = root
t = None
while node:
p = node.right
node.right = t
t = node
node = p
node = t
t = None
while node:
p = node.right
print(node.val)
node.right = t
t = node
node = p
def postorder(root):
if not root: return
p = root; prenode = None
while p:
if p.left:
prenode = p.left
while prenode.right and prenode.right != p:
prenode = prenode.right
if not prenode.right: #建立链接方便回溯
prenode.right = p
p = p.left
continue
else: #prenode.right == p
prenode.right = None #回溯完成删除多余链接
ReverseAndPrint(p.left)
p = p.right
ReverseAndPrint(root)