Morris遍历
先介绍一下Morris遍历的具体过程,至于先序、中序和后序就是在遍历中选择不同时机进行打印。
在Morris遍历中,我们假设当前节点为 cur,只要当前节点的左节点存在,顺着左节点的右边一直走到它的最右边界节点,
- 如果是第一次遍历到这个最右边界节点,那么这个边界节点的 right 应该指向 None,这里做一个标记,将它的 right 不再指向 None,改为指向当前的 cur 节点, 此时cur 下一步就应该来到cur的左节点;
- 如果不是第一次来到这个最右边界节点,那么最右边界的 right 应该是指向当前的 cur ,此时先将right恢复为指向 None, cur的下一步来到 cur 的右节点;
上面两条中,关键的一点就在于如何判断是不是第一次来到这个最右边界,所以将最右边界的 right 指向作一个修改,通过这个判断是不是第一次来到最右边界,从而决定接下来的 cur 的走向。毫无疑问,如果是第一次来到,那么 cur 往左节点走,一定会再次来到这个最右边界;如果不是第一次来到,那么接下来 cur 会往右走,一定不会再来到这个左节点的最右边界;
这是左节点存在时候的走向,如果左节点不存在,那就更简单了,cur 直接去往右节点,再继续判断。最后如果连右节点都不存在了,说明当前树已经遍历结束。整个流程结束。
整个遍历遵循先左再右的原则,左边遍历结束后,才会前往右边。
# 纯morris遍历,不带任何输出
def morris_tree(head):
if head == None: return None
cur = head
while (cur != None):
if cur.left != None:
most_right = cur.left
while (most_right.right != None and most_right.right is not cur): # 找到它的左子树的最右边界
most_right = most_right.right
if most_right.right == None: # 此时表示cur节点是第一次
most_right.right = cur
cur = cur.left
continue # 中断当前的while循环,进行下一次while
else: # 表明这是第二次访问cur节点了
most_right.right = None
cur = cur.right
先序遍历:
从最简单的递归遍历出发,仔细揣摩真个递归过程,其实不同的遍历顺序实质是选择不同的遍历时机进行输出打印,拿先序遍历来说,选择在第一次来到当前节点的时候,打印输出,就是先序遍历;中序就是选择在第二次时候打印,后序就是第三次。代码如下:
def process(head):
if head == None:
return head
print(head.value) # 先序
process(head.left)
# print(head.value) # 中序
process(head.right)
# print(head.value) # 后序
同样的在Morris遍历中,我们选择在第一次来到当前节点的时候打印,最终就是先序遍历;而在Morris遍历中,根据当前节点的左子树的最右边界的 right 是否指向当前节点来判断是第几次来到当前节点,如果不存在左子树的话,那么就要立即打印;
代码如下:
def morris_tree_preOrder(head):
if head == None: return None
cur = head
while (cur != None):
most_right = cur.left
if cur.left != None:
while (most_right.right != None and most_right.right is not cur):
most_right = most_right.right
if most_right.right == None:
most_right.right = cur
print(cur.value) # 如果cur左子树存在,它的左子树的最右边界没有指向cur也就是指向None的时候,表示它是第一次来到这个节点
cur = cur.left
continue
else:
most_right.right = None
else:
print(cur.value) # cur的左子树不存在,那么它再也不会回到cur,第一次也是最后一次
cur = cur.right
中序遍历
中序遍历也是同样道理,当前节点的左子树不存在或者是第二次来到当前节点时打印;
def morris_tree_inOrder(head):
if head == None: return None
cur = head
while (cur != None):
most_right = cur.left
if most_right != None:
while (most_right.right != None and most_right.right is not cur):
most_right = most_right.right
if most_right.right == None:
most_right.right = cur
cur = cur.left
continue
else:
most_right.right = None
print(cur.value)
else:
print(cur.value) # cur的左子树不存在,那么它再也不会回到cur,第一次也是最后一次
cur = cur.right
后序遍历
后序遍历稍微有些不同,在Morris遍历中,顺着每个节点的左子树,可以把它分成若干个右边界,我们选择在每次左子树存在的情况下,逆序打印它的右边界,就可以实现后序遍历;
def morris_tree_postOrder(head):
if head == None: return None
cur = head
while (cur != None):
most_right = cur.left
if cur.left != None:
while (most_right.right != None and most_right.right is not cur): # 找到它的左子树的最右边界
most_right = most_right.right
if most_right.right == None: # 此时表示cur节点是第一次
most_right.right = cur
cur = cur.left
continue # 中断当前的while循环,进行下一次while
else: # 表明这是第二次访问cur节点了
most_right.right = None
printEdge(cur.left) # 第二次访问cur节点时,逆序打印cur的左子树的右边界
cur = cur.right
printEdge(head) # 打印头结点的右边界
# 打印右边界
def printEdge(head):
tail = reverseEdge(head)
cur = tail
while (cur != None):
print(cur.value)
cur = cur.right
reverseEdge(tail)
# 反转链表
def reverseEdge(head):
pre = None
while head != None:
tmp = head.right
head.right = pre
pre = head
head = tmp
return pre