O-07/26/27/28 二叉树重建/子结构/镜像/对称(Python)

O-07 重建二叉树

题目

输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
给出:

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]

返回:

 3
   / \
  9  20
    /  \
   15   7

分析与代码

前序遍历性质: 节点按照 [ 根节点 | 左子树 | 右子树 ] 排序。
中序遍历性质: 节点按照 [ 左子树 | 根节点 | 右子树 ] 排序。
对于示例树,
前序遍历划分 [ 3 | 9 | 20 15 7 ]
中序遍历划分 [ 9 | 3 | 15 20 7 ]

可以直接根据递归来做。

方法1:

ind 可表示左子树的个数也可以表示在中序排列时候根节点的位置,以此来确定递归的index

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        if len(preorder)==0:
             return None
         root=TreeNode(preorder[0])
         ind=inorder.index(preorder[0])
        #构建左/右子树
         left=self.buildTree(preorder[1:ind+1],inorder[:ind])
         right=self.buildTree(preorder[ind+1:],inorder[ind+1:])
         root.left=left
         root.right=right
         return root

方法2

参考链接:link
在这里插入图片描述
通过以上三步,可确定 三个节点 :1.树的根节点、2.左子树根节点、3.右子树根节点。
对于树的左、右子树,仍可使用以上步骤划分子树的左右子树。

分治算法解析:

  • 递推参数: 根节点在前序遍历的索引 root 、子树在中序遍历的左边界 left 、子树在中序遍历的右边界 right ;

  • 终止条件: 当 left > right ,代表已经越过叶节点,此时返回 nullnull ;

  • 递推工作:

  • 建立根节点 node : 节点值为 preorder[root] ;

  • 划分左右子树: 查找根节点在中序遍历 inorder 中的索引 i ;为了提升效率,本文使用哈希表 dic 存储中序遍历的值与索引的映射,查找操作的时间复杂度为 O(1)

  • 构建左右子树: 开启左右子树递归;
    在这里插入图片描述

  • 返回值: 回溯返回 node ,作为上一层递归中根节点的左 / 右子节点

难理解的几点:
1.“in_left”意义是当前树的左边界,“in_right”即是右边界,即inorder_left_bound,inorder_right_bound。

2.“i”是根节点在中序遍历中的坐标,即"inorder_root"。

3.“i-in_left+preroot+1”,其实就是右子树根节点=(中序根节点坐标-中序左边界)+先序根节点坐标+1,其中括号内=左子树长度。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        def recur(root, left, right):
            if left > right: return                               # 递归终止
            node = TreeNode(preorder[root])                       # 建立根节点
            i = dic[preorder[root]]                               # 划分根节点左子树右子树
            node.left = recur(root + 1, left, i - 1)              # 开启左子树递归
            node.right = recur(i - left + root + 1, i + 1, right) # 开启右子树递归
            return node                                           # 回溯返回根节点

        dic, preorder = {}, preorder
        for i in range(len(inorder)):
            dic[inorder[i]] = i
        return recur(0, 0, len(inorder) - 1)

O-26 树的子结构

题目

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值。

例如:
给定的树 A:

     3
    / \
   4   5
  / \
 1   2
给定的树 B:

   4 
  /
 1
返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。

示例 1:
输入:A = [1,2,3], B = [3,1]
输出:false

示例 2:
输入:A = [3,4,5,1,2], B = [4,1]
输出:true

分析与代码

参考链接:匹配类二叉树算法总结
这类题目与字符串匹配有些神似,求解过程大致分为两步:

先将根节点匹配;

根节点匹配后,对子树进行匹配。

而参与匹配的二叉树可以是一棵,与自身匹配;也可以是两棵,即互相匹配。

比如 对称二叉树 就是两棵树之间的匹配问题。
在这里插入图片描述

求解思路可以分解为以下两步:
匹配根节点:首先在 A 中找到与 B 的根节点匹配的节点 C;
匹配其他节点:验证 C 的子树与 B 的子树是否匹配。

代码中主要涉及 主函数和 dfs 函数 两个部分。与以上思路对应,主函数对应根节点的匹配,dfs 函数对应匹配其他节点。

dfs 函数
dfs 函数将注意力集中在了根节点已经匹配的情况。当从根节点同时开始向下遍历时,我们进行以下判断:
在这里插入图片描述

如果 A 和 B 同时遍历到了 null,说明匹配成功,返回 True(case 1 红色虚线框);

如果 A 或 B 提前遍历到了 null,一棵树匹配完了,另一棵却没有,说明子树的结构是不同的,则匹配失败,返回 False(case 2 红色虚线框)
在这里插入图片描述

以上是“基本情况”,在到达基本情况之前,我们需要判断根节点的值、**左子树(调用递归)右子树(调用递归)**是否匹配

主函数

现在将视野放远来看,主函数则解决了如何确定 A 的哪个节点是 B 的根节点。
在这里插入图片描述

如果 A 的当前节点值与 B 的根节点值相同,我们调用 dfs 函数判断子树是否也相同;如果不同,我们就递归调用主函数来寻找 A 的哪个节点与 B 的根节点匹配。

主函数还经常需要判断以下边界条件,比如如果 A 为空,则肯定不匹配,返回 False。

对于本题来讲,与前面的例题很像,不同的是 B 属于 A 的一部分也可以,没必要一直匹配到叶子节点。因此只需对 check 函树的基本条件进行修改即可

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def check(self,a,b):
#先判断一下,需匹配的子树是否匹配完,匹配完则True
        if not b :return True
        if not a :return False
        return a.val==b.val and self.check(a.left,b.left) and self.check(a.right,b.right) 

    def isSubStructure(self, A: TreeNode, B: TreeNode) -> bool:
        if not A or not B: return False
        return self.check(A,B) or self.isSubStructure(A.left,B) or self.isSubStructure(A.right,B)

O-27 二叉树的镜像

题目

请完成一个函数,输入一个二叉树,该函数输出它的镜像。

例如输入:

     4
   /   \
  2     7
 / \   / \
1   3 6   9
镜像输出:

     4
   /   \
  7     2
 / \   / \
9   6 3   1

 

示例 1:

输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]

分析与代码

方法1 递归

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def mirrorTree(self, root: TreeNode) -> TreeNode:
        # 直接交换
         if not root:return None
         root.left,root.right=self.mirrorTree(root.right),self.mirrorTree(root.left)
         return root

方法2 辅助栈

参考链接:link
二叉树镜像定义: 对于二叉树中任意节点 root ,设其左 / 右子节点分别为 left, right ;则在二叉树的镜像中的对应 root节点,其左 / 右子节点分别为 right, left。
在这里插入图片描述
利用栈(或队列)遍历树的所有节点 node ,并交换每个 node 的左 / 右子节点。
算法流程:

  • 特例处理: 当 root 为空时,直接返回 null ;
  • 初始化: 栈(或队列),本文用栈,并加入根节点 root。
  • 循环交换: 当栈 stack 为空时跳出;
  • 出栈: 记为 node ;
  • 添加子节点: 将 node左和右子节点入栈;
  • 交换: 交换 node 的左 / 右子节点。
  • 返回值: 返回根节点root 。
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def mirrorTree(self, root: TreeNode) -> TreeNode:
        # 辅助栈
        if not root:return None
        stack=[root]
        while stack:
            node=stack.pop()
            if node.left:stack.append(node.left)
            if node.right:stack.append(node.right)
            node.left,node.right=node.right,node.left
        return root


O-28 对称的二叉树

题目

请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

    1
   / \
  2   2
 / \ / \
3  4 4  3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

    1
   / \
  2   2
   \   \
   3    3

 

示例 1:

输入:root = [1,2,2,3,4,4,3]
输出:true
示例 2:

输入:root = [1,2,2,null,3,null,3]
输出:false

分析与代码

参考链接:link
思路:
对称二叉树定义: 对于树中 任意两个对称节点 L和 R,一定有:

  • L.val = R.val:即此两对称节点值相等。
  • L.left.val = R.right.val:即L的左子节点和R的右子节点对称;
  • L.right.val = R.left.val:即L的右子节点和R的左子节点对称。

根据以上规律,考虑从顶至底递归,判断每对节点是否对称,从而判断树是否为对称二叉树
在这里插入图片描述

isSymmetric(root) :
特例处理: 若根节点 root 为空,则直接返回 true 。
返回值: 即 recur(root.left, root.right) ;

recur(L, R) :

  • 终止条件:
    当 L和 R同时越过叶节点: 此树从顶至底的节点都对称,因此返回 true ;
    当 L或 R只有一个越过叶节点: 此树不对称,因此返回 false ;
    当节点 L值! =节点 R 值: 此树不对称,因此返回 false ;
  • 递推工作:
    判断两节点L.left 和R.right 是否对称,即recur(L.left, R.right) ;
    判断两节点 L.right 和 R.left 是否对称,即 recur(L.right, R.left) ;
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        def recur(left,right):
            if not left and not right: return True
            if not left or not right or left.val!=right.val:return False
            return recur(left.left,right.right)and recur(left.right,right.left)
        return recur(root.left,root.right) if root else True

—2020/01/15–二叉树/递归----

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值