返回

纵览LeetCode 114:一文掌握Morris Traversal,将二叉树巧妙转化为链表!

后端

LeetCode 114:二叉树展开为链表

在 LeetCode 114 中,你将面临一项看似复杂的任务:将一棵二叉树展开为一个链表。虽然这个任务听起来很艰巨,但它实际上可以分解成几个简单的步骤,而 Morris Traversal 和一种特殊的先序遍历正是实现这一目标的两种有效方法。

Morris Traversal:巧妙避开递归和栈

Morris Traversal 是一种巧妙的遍历方法,它巧妙地避开了递归和栈的使用,极大地提高了算法的效率。该算法的核心思想是:在遍历二叉树的过程中,当我们访问到一个节点时,如果它的右子树为空,则我们将它的左子树作为它的右子树,否则,我们将它的右子树中最左边的节点作为它的右子树。通过这种方式,我们就能将二叉树展开为一个链表。

特殊的先序遍历:从根节点开始的深度优先搜索

另一种将二叉树展开为链表的方法是使用一种特殊的先序遍历。这种遍历方法与普通的先序遍历略有不同:它在访问一个节点的左子树之前,先将它的右子树作为它的右子树。通过这种方式,我们也能将二叉树展开为一个链表。

比较两种方法:

Morris Traversal 和特殊的先序遍历都是将二叉树展开为链表的有效方法。然而,这两种方法各有优缺点。Morris Traversal 的优点在于它不需要使用递归和栈,因此它的空间复杂度更低。然而,它的缺点在于它的代码实现可能更复杂一些。特殊的先序遍历的优点在于它的代码实现更简单,但它的缺点在于它的空间复杂度更高。

理解代码实现

无论你选择哪种方法,你都需要理解代码的实现才能正确地将二叉树展开为链表。这里,我们将以 Morris Traversal 为例,来讲解代码的实现。

def flatten(root):
    if not root:
        return

    while root:
        # 如果当前节点没有右子树,则将它的左子树作为它的右子树
        if not root.right:
            root.right = root.left
            root.left = None
        # 否则,将它的右子树中最左边的节点作为它的右子树
        else:
            predecessor = root.right
            while predecessor.left:
                predecessor = predecessor.left
            predecessor.left = root.left
            root.left = None
        # 将当前节点移动到它的右子树
        root = root.right

代码解释:

  1. 首先,我们对二叉树的根节点进行判空。如果根节点为空,则直接返回,因为没有必要进行后续操作。
  2. 接下来,我们使用一个 while 循环来遍历二叉树。在每次循环中,我们首先检查当前节点是否具有右子树。
  3. 如果当前节点没有右子树,则我们将它的左子树作为它的右子树,并将它的左子树设置为 None。
  4. 否则,我们将它的右子树中最左边的节点作为它的右子树。为了找到这个最左边的节点,我们需要使用一个 while 循环来遍历它的右子树。
  5. 最后,我们将当前节点移动到它的右子树,并继续进行下一次循环。

通过这种方式,我们将二叉树展开为一个链表。

总结

LeetCode 114 是一个经典的二叉树操作题目,它考察了我们对二叉树和链表的理解以及对算法的掌握程度。通过本文,我们介绍了两种将二叉树展开为链表的方法:Morris Traversal 和一种特殊的先序遍历。这两种方法各有优缺点,但都能够有效地解决问题。我们还对 Morris Traversal 的代码实现进行了详细的讲解,帮助你更好地理解算法的细节。