📜  使用Morris遍历遍历二叉树的级序。(1)

📅  最后修改于: 2023-12-03 15:22:19.183000             🧑  作者: Mango

Morris遍历二叉树

什么是Morris遍历?

Morris遍历是一种利用线索二叉树进行遍历的算法。这种算法的目的是在不使用栈或递归的情况下,实现对二叉树的遍历。

Morris遍历算法的核心思想是在二叉树中添线索,将空闲的指针指向前驱或后继,从而实现在不额外开辟空间的情况下找到二叉树的前驱或后继节点。

Morris遍历的实现
Morris遍历的前序遍历

Morris遍历的前序遍历可以按以下步骤实现:

  1. 如果当前节点为空,返回;
  2. 如果当前节点没有左子树,输出当前节点的值,然后将指针移动到右子树;
  3. 如果当前节点有左子树,找到当前节点的前驱节点;
  4. 如果前驱节点的右指针为空,将其指向当前节点,然后输出当前节点的值,然后将指针移动到左子树;
  5. 如果前驱节点的右指针指向当前节点,将其置空,然后将指针移动到右子树。

代码实现如下:

def MorrisPreTraversal(root):
    """
    Morris遍历的前序遍历
    :param root: 树的根节点
    :return: 前序遍历结果
    """
    result = []
    curr = root
    while curr:
        if not curr.left:
            result.append(curr.val)
            curr = curr.right
        else:
            # 找到前驱节点
            pre = curr.left
            while pre.right and pre.right != curr:
                pre = pre.right
            # 前驱节点的右指针为空,将其指向当前节点
            if not pre.right:
                pre.right = curr
                result.append(curr.val)
                curr = curr.left
            # 前驱节点的右指针指向当前节点,将其置空
            else:
                pre.right = None
                curr = curr.right
    return result
Morris遍历的中序遍历

Morris遍历的中序遍历可以按以下步骤实现:

  1. 如果当前节点为空,返回;
  2. 如果当前节点没有左子树,输出当前节点的值,然后将指针移动到右子树;
  3. 如果当前节点有左子树,找到当前节点的前驱节点;
  4. 如果前驱节点的右指针为空,将其指向当前节点,然后将指针移动到左子树;
  5. 如果前驱节点的右指针指向当前节点,将其置空,输出当前节点的值,然后将指针移动到右子树。

代码实现如下:

def MorrisInTraversal(root):
    """
    Morris遍历的中序遍历
    :param root: 树的根节点
    :return: 中序遍历结果
    """
    result = []
    curr = root
    while curr:
        if not curr.left:
            result.append(curr.val)
            curr = curr.right
        else:
            # 找到前驱节点
            pre = curr.left
            while pre.right and pre.right != curr:
                pre = pre.right
            # 前驱节点的右指针为空,将其指向当前节点
            if not pre.right:
                pre.right = curr
                curr = curr.left
            # 前驱节点的右指针指向当前节点,将其置空
            else:
                pre.right = None
                result.append(curr.val)
                curr = curr.right
    return result
Morris遍历的后序遍历

Morris遍历的后序遍历可能是最复杂的一种情况,需要对前序遍历稍作修改,具体步骤如下:

  1. 如果当前节点为空,返回;
  2. 如果当前节点没有左子树,将其指针移动到右子树;
  3. 如果当前节点有左子树,找到当前节点的前驱节点;
  4. 如果前驱节点的右指针为空,将其指向当前节点,然后将指针移动到左子树;
  5. 如果前驱节点的右指针指向当前节点,将其置空,将前驱节点到当前节点这个区间的节点逆序输出,然后将指针移动到右子树。

代码实现如下:

def reverse(node):
    """
    反转从起始节点到终止节点之间的节点
    :param node: 起始节点
    :return: 反转后的起始节点
    """
    pre, curr = None, node
    while curr:
        next = curr.right
        curr.right = pre
        pre, curr = curr, next
    return pre

def MorrisPostTraversal(root):
    """
    Morris遍历的后序遍历
    :param root: 树的根节点
    :return: 后序遍历结果
    """
    result = []
    dummy = TreeNode(-1)
    dummy.left = root
    curr = dummy
    while curr:
        if not curr.left:
            curr = curr.right
        else:
            # 找到前驱节点
            pre = curr.left
            while pre.right and pre.right != curr:
                pre = pre.right
            # 前驱节点的右指针为空,将其指向当前节点
            if not pre.right:
                pre.right = curr
                curr = curr.left
            # 前驱节点的右指针指向当前节点,将其置空
            else:
                pre.right = None
                reverse(curr.left)
                node = pre
                while node:
                    result.append(node.val)
                    node = node.right
                reverse(curr.left)
                curr = curr.right
    return result
总结

Morris遍历算法利用线索二叉树实现遍历,不需要开辟额外的空间,可以在O(n)时间内完成遍历。虽然算法实现稍显复杂,但在实际应用中受到广泛关注。