7.3.1 二叉树遍历的应用及LeetCode题目解析(1)

这一节,我们看一下应用二叉树的遍历,可以解决哪些问题,如何应用这些原理刷题。花式遍历,加上我们接下来解析的题目,可以基本涵盖数据结构二叉树这部分的主要内容了。还是要对其中难点好好练习。

其实,在LeetCode中有几道题目就是遍历二叉树, 如

94

Binary Tree Inorder Traversal

144

Binary Tree Preorder Traversal

145

Binary Tree Postorder Traversal

102

Binary Tree Level Order Traversal

107

Binary Tree Level Order Traversal II

 说明我们上一节其实也是在解析LeetCode的题目哈 →. →

下面我们看教材上的一些经典问题:

1)输出二叉树的所有叶子结点;

先序遍历,判断一下是否叶子。

2)把二叉树T1复制到二叉树T2中;

递归解决。

3)把一个二叉树的左右子树进行交换,不破坏原二叉树;

同上

4)求二叉树中值为x的结点所在层次;

层次遍历,每层计数;或者递归调用函数,递归一次加一层。

5)输出一个二叉树从根结点到每个叶子结点的逆路径;

上一节有个问题,就是每种遍历蕴含哪些信息,其中最重要的一点就是后序遍历过程中,当访问某个元素时,栈中元素就是该元素的所有祖先,即路径。在这一类问题上,遍历时我们需要关注遍历过程中栈内元素。后面我们会对路径问题展开解析。

对于后序遍历,遍历过程中除了包含结点的访问顺序,还有访问该结点时该结点栈中元素等“半路”信息,这种信息十分重要(后序遍历是二叉树路径问题的关键),要学会方法一;方法二中stack2只剩下后序遍历的结点的顺序了,不含有任何中间过程。

6)求二叉树的宽度

层次遍历,比较每层队列的长度即可。

7)二叉树的构造

需要重点提的是二叉树的构造:

这里面竟然还涉及两个定理,总结一句就是 n个不同结点的二叉树,可由中序序列先序序列 中序序列和后序序列 唯一的确定。

显然,单一的一个遍历序列,或者先序和后序序列,都无法确定2个结点及以上的二叉树。二叉树的构造,就是根据 中序序列和先序序列  中序序列和后序序列 构建一棵二叉树。这也是LeetCode上面的原题。我们后面进行解析。


以上是教材中的重要题目及简单讲解,下面我们对LeetCode中题目归类汇总,讲解二叉树这部分的题目。

(一) 二叉树遍历简单应用

简单先序遍历无难度的几道题。基本都是一遍过。

100. Same Tree

Given two binary trees, write a function to check if they are the same or not.

Two binary trees are considered the same if they are structurally identical and the nodes have the same value.

题目解析:

写了一个递归的方法,对于p和q ,if判断语句是判断两个结点为空是否一致,都不为空,再按结点值-左子树-右子树的顺序依次判断。

class Solution:
    def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
        if p and q:
            if p.val != q.val:
                return False
            if not self.isSameTree(p.left, q.left):
                return False
            if not self.isSameTree(p.right, q.right):
                return False
        elif p and not q or q and not p:
            return False
        return True

101. Symmetric Tree

Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).

For example, this binary tree [1,2,2,3,4,4,3] is symmetric:

题目解析:

这一问题,对称的话一定是根结点为对称轴,然后递归判断左右子树是否对称。写了一个函数,递归判断pq结点值-p左及q右-p右及q左。

class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        if not root:
            return True
        
        # 通过中序遍历序列结果来判断是错误的
        p = root.left        
        q = root.right
        
        def symm(p, q):
            if not p and q or not q and p:
                return False
            if p and q:
                if p.val != q.val:
                    return False
                s = symm(p.left, q.right)
                if not s:
                    return s
                s = symm(p.right, q.left)
                if not s:
                    return s
            return True
        
        return symm(p, q)

111. Minimum Depth of Binary Tree

Given a binary tree, find its minimum depth.

The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node.

Note: A leaf is a node with no children.

题目解析:

树高度问题的变种,求高度最小的叶子的结点的高度。毫无压力,看代码。

class Solution:
    def minDepth(self, root: TreeNode) -> int:
        if not root:
            return 0
        
        h1, h2 = 0, 0
        if not root.left and not root.right:
            return 1
        
        if root.left:
            h1 = self.minDepth(root.left)
        if root.right:
            h2 = self.minDepth(root.right)        
        
        if h1 and not h2:
            return h1 + 1
        elif h2 and not h1:
            return h2 + 1
        else:
            return min(h1, h2) + 1

222. Count Complete Tree Nodes 

Given a complete binary tree, count the number of nodes.

Note:

Definition of a complete binary tree from Wikipedia:
In a complete binary tree every level, except possibly the last, is completely filled, and all nodes in the last level are as far left as possible. It can have between 1 and 2h nodes inclusive at the last level h.

题目解析:

题目不难,题目中给出了最核心的思路。第一种方法是先序遍历一遍计算结点数目,明显忽略了完全二叉树的特性,不过性能还不错。但是,在非数组存储结构中,我们如何应用完全二叉树的性质呢?方法二,根据树的深度来计算结点数目。性质1:完全二叉树的深度计算,看最左的叶子即可。 性质2:当左子树与右子树深度相同时,左子树一定是满二叉,因此用2**h直接计算,然后再递归解决右子树;当左右不同时,右子树少一层,一定是满树,左子树不一定满,递归计算。

class Solution:
    def countNodes(self, root: TreeNode) -> int:        
        if not root:
                return 0
        leftDepth = self.getDepth(root.left)
        rightDepth = self.getDepth(root.right)
        if leftDepth == rightDepth:
            return pow(2, leftDepth) + self.countNodes(root.right)
        else:
            return pow(2, rightDepth) + self.countNodes(root.left)

    def getDepth(self, root):
        if not root:
            return 0
        return 1 + self.getDepth(root.left)

 

都属于极简单的题目,解决方法不唯一,但是思路基本是这样的。

 

(二)二叉树的构造

我们再来看一下二叉树构造的两个题目。

105

Construct Binary Tree from Preorder and Inorder Traversal

106

Construct Binary Tree from Inorder and Postorder Traversal

 题目解析:

以先序序列和中序序列进行二叉树构造为例,总体思路其实很简单,先序序列确定根结点,根结点在中序遍历中将左右子树分开,于是有了根节点左、右子树的先序序列和中序序列,这样我们就递归的得到了每个子树的先序序列和中序序列。该问题用递归解法十分简洁易懂,代码如下。

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        l = len(preorder)
        if not l:
            return None
        
        root = TreeNode(preorder[0])
        for ix, val in enumerate(inorder):
            if val == preorder[0]:
                break  # 代表左子树结点数为ix个
        # ix = inorder.index(preorder[0])  # 这一行代码代替上面遍历的代码,提高很高效率
        # rt_len = l-ix-1  # 右子树结点数, 用不到
        root.left = self.buildTree(preorder[1:ix+1], inorder[:ix])
        root.right = self.buildTree(preorder[ix+1:], inorder[ix+1:])
        
        return root

 仅仅几行,这是教材中代码的python实现,但是在LeetCode中如果你关注代码的性能,你会看到这种方法,耗时垫底。那么一定还有更快更好的方法咯~

看下面的代码,抄袭一位大神的结果,耗时和内存都很不错的,其实解题思路并没有变,思考一下上面方法耗时耗内存的原因,莫过于列表切片操作吧,下面的代码,另写一个函数,传入列表中的相应索引,没有切片拷贝的过程,想必优秀在这里吧。

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        def buildNode(i, j):
            # base case
            if i == j: return None

            parent = TreeNode(preorder.pop(0))
            ix = inorder.index(parent.val)

            parent.left = buildNode(i, ix)
            parent.right = buildNode(ix + 1, j)

            return parent
        
        return buildNode(0, len(preorder))

对于二叉树的构造,如果还有什么高见也欢迎大家指教哈。


这一节简单介绍二叉树遍历的常见问题,以及LeetCode中比较简单的一些题目,更难的内容还要看后面啦。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值