剑指offer-树专题

重建二叉树

思路

用递归的方式解决:

  • Root是preorder的首项,在inorder里面找到root对应的位置,即可以确定左右子树。
  • 左子树的root是pre_root的下一项;依然根据root的位置确定root在左子树inorder的顺序,之后一直递归下去。
  • 右子树的root是preorder中右子树的第一项:pre_root+1+i-in_left。根据这个递归下去。

代码

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        self.dic, self.po = {}, preorder
        for i in range(len(inorder)):
            self.dic[inorder[i]] = i
        return self.recur(0, 0, len(inorder) - 1)

    def recur(self, pre_root, in_left, in_right):
        if in_left > in_right: return # 终止条件:中序遍历为空
        root = TreeNode(self.po[pre_root]) # 建立当前子树的根节点
        i = self.dic[self.po[pre_root]]    # 搜索根节点在中序遍历中的索引,从而可对根节点、左子树、右子树完成划分。
        root.left = self.recur(pre_root + 1, in_left, i - 1) # 开启左子树的下层递归
        root.right = self.recur(i - in_left + pre_root + 1, i + 1, in_right) # 开启右子树的下层递归
        return root # 返回根节点,作为上层递归的左(右)子节点

相似题

106. 从中序与后序遍历序列构造二叉树
代码

# 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, inorder: List[int], postorder: List[int]) -> TreeNode:
        self.dic, self.po = {}, postorder 
        for i in range(len(inorder)):
            self.dic[inorder[i]] = i
        return self.recur(len(postorder)-1, 0, len(inorder)-1)

    def recur(self, post_root, in_left, in_right):
        if in_left>in_right : return None
        # 找根节点
        root_node = TreeNode(self.po[post_root])
        if in_left == in_right: 
            return root_node
        # 找根节点在inorder中的位置
        i = self.dic[self.po[post_root]]
        print(i)
        # 递归
        root_node.right = self.recur(post_root-1, i+1, in_right)
        # 左子树考虑清楚,是post_root-(右子树的长度)-1
        root_node.left = self.recur(post_root-in_right+i-1, in_left, i-1)
        
        return root_node



剑指 Offer 26. 树的子结构

思路

经典的“匹配类二叉树问题”。有套路可循:

  • 先匹配根节点
  • 匹配根节点之后对子树进行匹配

在这道题中,写两个函数分别完成这两个功能:dfs 对子树进行匹配;isSubStructure为主函数,对应根节点的匹配。

代码

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

class Solution:
    def dfs(self, a, b):
        if not b: return True
        if not a: return False
        else:
            return a.val == b.val and self.dfs(a.left, b.left) and self.dfs(a.right, b.right)
    def isSubStructure(self, A: TreeNode, B: TreeNode) -> bool:
        if not A or not B: return False
        return self.dfs(A, B) or self.isSubStructure(A.left, B) or self.isSubStructure(A.right, B)

       

思考

一道题解决一类题,我们可以用这个思路解决一系列“匹配类二叉树”的题目。

101. 对称二叉树

思路

同上

代码

class Solution:
    def dfs(self, a, b):
        if not a and not b: return True
        elif not a or not b: return False
        return a.val == b.val and self.dfs(a.left, b.right) and self.dfs(a.right, b.left)
    def isSymmetric(self, root: TreeNode) -> bool:
        if not root: return True
        return self.dfs(root.left, root.right)

1367. 二叉树中的列表

思路

只是将其中一个树换成了链表,实际上还是匹配根节点,再匹配子树的套路。

代码

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

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

class Solution:
    def dfs(self, a, b):
        if not a : return True
        if not b : return False
        return b.val == a.val and \
        (self.dfs(a.next, b.left) or self.dfs(a.next, b.right))
    def isSubPath(self, head: ListNode, root: TreeNode) -> bool:
        if not root or not head: return False
        return self.dfs(head, root) or self.isSubPath(head, root.left) or self.isSubPath(head, root.right)

572. 另一个树的子树

思路

同上

代码

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def dfs(self, s, t):
        if not s and not t: return True
        elif not s or not t: return False
        return s.val == t.val and self.dfs(s.left, t.left) and self.dfs(s.right, t.right)
    def isSubtree(self, s: TreeNode, t: TreeNode) -> bool:
        if not s or not t: return False
        return self.dfs(s, t) or self.isSubtree(s.left, t) or self.isSubtree(s.right, t)

剑指 Offer 27. 二叉树的镜像

思路

  • 用递归的方法解决:在不None的情况下,新左右等于旧右左。

代码

# 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

剑指 Offer 32 - I. 从上到下打印二叉树

思路

  • 用BFS的方式,即采用queue结构来保存,之后依次写入res,最后打印出来。相当直接。

代码

import queue
class Solution:
    def levelOrder(self, root: TreeNode) -> List[int]:
        if not root: return []
        q = queue.deque()
        res = []
        q.append(root)
        while q:
            out = q.popleft()
            res.append(out.val)
            if out.left: q.append(out.left)
            if out.right: q.append(out.right)
            
        return res

思考

这里需要注意,queue库中,Queue是依靠List实现的,所以put和insert的平均读写时间是 O ( n ) O(n) O(n). 而deque双向队列是 O ( 1 ) O(1) O(1)。所以用Queue写会超时。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kG3sCswf-1601058243643)(evernotecid://BC7CFB48-5C18-439C-ADB2-AD1171DE2741/appyinxiangcom/18838120/ENResource/p670)]

剑指 Offer 32 - II. 从上到下打印二叉树 II

思路:

同上,不过有一点点改变:在每次循环中把val写入这一层的tmp list,最后append tmp

代码:

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

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        if not root: return []
        res, q = [], collections.deque()
        q.append(root)
        while q:
            tmp = []
            for _ in range(len(q)):
                out = q.popleft()
                if out.left: q.append(out.left)
                if out.right: q.append(out.right)
                tmp.append(out.val)
            res.append(tmp)
        return res

思考:

一定要把边界条件放在最开始考虑

剑指 Offer 32 - III. 从上到下打印二叉树 III

思路

同上,不过每奇数层做一个翻转操作。

代码

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

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        if not root: return []
        res, q = [], collections.deque()
        l = 0
        q.append(root)
        while q:
            tmp = []
            for _ in range(len(q)):
                out = q.popleft()
                if out.left: q.append(out.left)
                if out.right: q.append(out.right)
                tmp.append(out.val)
            if l%2 == 1:
                print(tmp)
                for p in range(len(tmp)):
                    tmp.insert(p, tmp.pop())
            l += 1
            res.append(tmp)
        return res

剑指 Offer 34. 二叉树中和为某一值的路径

思路:

本问题是典型的二叉树方案搜索问题,使用回溯法解决,其包含 先序遍历 + 路径记录 两部分。

  • 先序遍历: 按照 “根、左、右” 的顺序,遍历树的所有节点。

  • 路径记录: 在先序遍历中,记录从根节点到当前节点的路径。当路径为 ① 根节点到叶节点形成的路径 且 ② 各节点值的和等于目标值 sum 时,将此路径加入结果列表。

算法流程:
pathSum(root, sum) 函数:

初始化: 结果列表 res ,路径列表 path 。
返回值: 返回 res 即可。

recur(root, tar) 函数:

递推参数: 当前节点 root ,当前目标值 tar 。
终止条件: 若节点 root 为空,则直接返回。
递推工作:

  • 路径更新: 将当前节点值 root.val 加入路径 path ;

  • 目标值更新: tar = tar - root.val(即目标值 tar 从 sum 减至 00 );

  • 路径记录: 当 ① root 为叶节点 且 ② 路径和等于目标值 ,则将此路径 path 加入 res 。

  • 先序遍历: 递归左 / 右子节点。

  • 路径恢复: 向上回溯前,需要将当前节点从路径 path 中删除,即执行 path.pop() 。

代码:

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

class Solution:
    def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
        res,path = [],[]
        dif = sum
        def recur(root, dif):
            if not root: return None
            path.append(root.val)
            dif -= root.val
            if dif == 0 and not root.left and not root.right:
            # 这里如果不用list强制构造新list,就会导致res里面加入的是path,是一直变化的。
                res.append(list(path))
            recur(root.left, dif)
            recur(root.right, dif)
            path.pop()
        recur(root, dif)
        return res

思考:

在def里面定义def,能够帮助理清思路。而且不用调用self,局部变量可以直接在内部函数里面调用。backtracking里面要注意,在路径恢复的时候要pop掉当前节点才能返回上一步。

剑指 Offer 54. 二叉搜索树的第k大节点

思路

  • 一个特殊的性质:BST的inorder traversal是从小到大排列好的。
  • 那么这道题就可以从inorder的逆序列。

代码

class Solution:
    def kthLargest(self, root: TreeNode, k: int) -> int:
        def dfs(root):
            if not root: return
            dfs(root.right)
            if self.k == 0: return
            self.k -= 1
            if self.k == 0: self.res = root.val
            dfs(root.left)

        self.k = k
        dfs(root)
        return self.res

剑指 Offer 55 - I. 二叉树的深度

思路

  • 第一个想到就是BFS,然后之前做的按层level print。可以输出这个res的长度。但非常不直接。
  • 也可以用DFS:深度就等于当前节点左/右子树的深度最大值+1。

代码

BFS:

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

class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        if not root:
            return 0
        # bfs, queue
        res, q = [], collections.deque()
        q.append(root)
        while q:
            tmp = []
            for _ in range(len(q)):
                out = q.popleft()
                tmp.append(out.val)
                if out.left: q.append(out.left)
                if out.right: q.append(out.right)
            res.append(tmp)
        return len(res)

DFS:

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

class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        if not root: return 0
        return max(self.maxDepth(root.left), self.maxDepth(root.right))+1

剑指 Offer 55 - II. 平衡二叉树

思路:

  • 可以用上面最大深度的function,先算出来左右子树的最大深度,再递归地判断当前是不是一个平衡二叉树。时间复杂度很高O(N*logn)
  • 剪枝

代码

简单算法:

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

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
    
        def get_depth(root):
            if not root: return 0
            return max(get_depth(root.left), get_depth(root.right))+1
        if not root: return True
        
        left_depth, right_depth= get_depth(root.left), get_depth(root.right)
        if abs(left_depth - right_depth) <= 1:
            return self.isBalanced(root.left) and self.isBalanced(root.right)
        else: return False
            

剪枝算法:

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

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        def recur(root):
            if not root: return 0
            left = recur(root.left)
            if left == -1: return -1
            right = recur(root.right)
            if right == -1: return -1
            if abs(left-right) >1 : return -1
            else : return max(left, right)+1
        return recur(root) != -1
        

剑指 Offer 68 - I. 二叉搜索树的最近公共祖先

思路:

  • 递归的方法解决:在以下三种情况下返回的是root值:
    1. p, q分别在root的两侧
    2. p是root
    3. q是root
  • 那么当p,q都在右侧时,递归右子树;vice versa。

代码:

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

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        p_val, q_val = p.val, q.val
        if not root: return 
        if (root.val-p_val)*(root.val-q_val) <= 0:
            return root
        elif p_val > root.val and q_val > root.val: 
            return self.lowestCommonAncestor(root.right, p, q)
        elif p_val < root.val and q_val < root.val: 
            return self.lowestCommonAncestor(root.left, p, q) 

剑指 Offer 68 - II. 二叉树的最近公共祖先

思路:

  • 用inorder遍历,得到的list可以判断左右子树的关系。那么就可以跟上一题一样,进行迭代。

代码

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

class Solution:
    def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        if not root: return None
        in_list = []
        dic = {}
        def inorder(root):
            if not root: return 
            inorder(root.left)
            in_list.append(root.val)
            inorder(root.right)
        inorder(root)
        for i in range(len(in_list)):
            dic[in_list[i]] = i
        p_c, q_c = dic[p.val], dic[q.val]
        while root:
            root_c = dic[root.val]
            if (p_c - root_c)*(q_c - root_c) <= 0 : return root
            if p_c<root_c and q_c < root_c : root = root.left
            if p_c > root_c and q_c > root_c : root = root.right
            

            
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值