剑指offer——二叉树题目集合(Python)

树的题目的技巧:树的题几乎都可以用递归(除了广度优先),做这类题目,非常重要的是知道如何访问二叉树!所以一定要会前序、中序和后续,要非常了解!(后续有时间会加上初步接触者的如何理解以及自己曾遇到哪些想不通的地方)

1 重建二叉树

题目描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

解题关键:前序:根->左->右,中序:左->根->右,根据前序找到根,中序的根的左子数,右边是右子树.二叉树的题几乎都可以用递归解决.根据递归思想,分解的最小的问题的解决思路是,给定值,构建二叉树根节点,根节点左子树等于?根节点右子树等于?代码思路:

第一步定义终止条件;第二步构建跟,第三步,左子树赋值,第四步,右子树赋值.代码如下:

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回构造的TreeNode根节点
    def reConstructBinaryTree(self, pre, tin):
        if not pre or not tin:
            return None
        root = TreeNode(pre[0])
        rootval = pre[0]
        p = self.find(pre[0],tin)
        if p != None:
            root.left = self.reConstructBinaryTree(pre[1:p+1], tin[:p])
            root.right = self.reConstructBinaryTree(pre[p+1:], tin[p+1:])
        return root
    
    def find(self,val,tin):
        for j in range(len(tin)):
            if tin[j] == val:
                return j
        return None

2 树的子结构

题目描述

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

解题关键:若B是A的子结构,则在A中肯定有某个子节点等于B的根,且A的某个子节点左等于B的左,右等于B的右.因此可以定义额外的函数,该函数判断A和B是否相同.再挨个遍历A,当A的某个节点等于B的根,则调用这个函数

第一步定义终止条件;第二步构建跟,第三步,左子树赋值,第四步,右子树赋值.代码如下(牛客网为觉得测试用例有点问题,为在本地调通的代码在牛客网上是错误的,但在牛客网上调通的代码,在本地是错误的,以下代码为本地代码):

# -*- coding:utf-8 -*-

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

def istree(pRoot1, pRoot2):
    if not pRoot2:
        return True
    if not pRoot1 and pRoot2:
        return False
    if pRoot1.val != pRoot2.val:
        return False
    elif pRoot1.val == pRoot2.val:
        return istree(pRoot1.left, pRoot2.left) and istree(pRoot1.right, pRoot2.right)

def HasSubtree(pRoot1, pRoot2):
    if not pRoot1 or not pRoot2:
        return False

    if pRoot1.val == pRoot2.val:
        return istree(pRoot1, pRoot2)
    else:
        return HasSubtree(pRoot1.left, pRoot2) or HasSubtree(pRoot1.right, pRoot2)

# {8,8,7,9,3,#,#,#,#,4,7},{8,9,2}


pRoot1 = TreeNode(8)
pRoot1.left = TreeNode(8)
pRoot1.left.left = TreeNode(7)
pRoot1.left.left.left = TreeNode(9)
pRoot1.left.left.left.left = TreeNode(3)
pRoot1.right = TreeNode(4)
pRoot1.right.left = TreeNode(7)

pRoot2 = TreeNode(8)
pRoot2.left = TreeNode(9)
pRoot2.left.left = TreeNode(2)

print(HasSubtree(pRoot1, pRoot2))

3 二叉树的镜像

题目描述

操作给定的二叉树,将其变换为源二叉树的镜像。

解题思路:从上到下地左<-->右

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回镜像树的根节点
    def Mirror(self, root):
        if not root:
            return None
        temp = root.left
        root.left = root.right
        root.right = temp
        self.Mirror(root.left)
        self.Mirror(root.right)
        return root

4 二叉搜索树的后续遍历

题目描述

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

解题思路:搜索数的特点:左<根<右;后续遍历:左,右,根.则数组最后一个数是根,则数组必须满足:1)不存在左,则所有的数均大于最后一个数;2)不存在右,所有数小于最后一个数;3)左右存在,则存在k,使得k之前的数小于最后一个数,k之后的数大于最后一个数.

class Solution:
    def VerifySquenceOfBST(self, sequence):
        if not sequence:
            return False
        i= 0
        for i in range(len(sequence)-1):
            if sequence[i]>sequence[-1]:
                break
        if i < len(sequence)-2:
            for j in range(i+1,len(sequence)-1):
                if sequence[j]<sequence[-1]:
                    return False
        left = True
        right = True
        if i>0:
            left = self.VerifySquenceOfBST(sequence[:i])
        elif i< len(sequence)-2:
            right = self.VerifySquenceOfBST(sequence[i:-1])
        return left and right

5 二叉树中和为某一值的路径

题目描述

输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

解题思路:题目定义了路径是从根节点到叶节点,所以会容易一些.从根往前走,走到叶节点,看和是否满足条件,满足就保存路径,不满足就返回走到下一个节点.python 根c++不太一样的是,c++到叶节点的时候往回走需要更新路径和当前和,单python往回走的适合,值会自动更新.代码如下:

class Solution:
    # 返回二维列表,内部每个列表表示找到的路径
    def __init__(self):
        self.paths = []
    def subFind(self,root, expectNumber,path):
        if not root:
            return
        path.append(root.val)
        expectNumber = expectNumber-root.val
        if root.left == None and root.right == None and expectNumber==0:
            self.paths.append(path)
        self.subFind(root.left, expectNumber,path[:])
        self.subFind(root.right, expectNumber,path[:])
        
        
    def FindPath(self, root, expectNumber):
        if not root:
            return []
        path = []
        self.subFind(root, expectNumber,path)
        return self.paths

6 二叉搜索树与双向链表

题目描述

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

解题思路,先思考一下,把二叉树变成双向链表,链表的头尾节点应该是哪个?这和访问方式有光.假设是前序,则头是根节点.中序和后序则头节点是叶节点.考虑到头尾有个指针指向none,我们考虑中序或后续,这样不用再处理根节点.

思路一般为:pre指向 cur,cur指向pre.由于用递归,不用跟链表一样保存下一个节点.而pre如何确定?只要找到头节点,保存下来,就是下个节点的pre.具体看代码.需要注意的是pre也要定义为全局变量,否则递归退回到上个节点的适合这个变量是不存在的.

class Solution:
    def __init__(self):
        self.flag = True
        self.pre = TreeNode(0)
    def Convert(self, pRootOfTree):
        if not pRootOfTree:
            return None
            self.Convert(pRootOfTree.left)
        self.Convert(pRootOfTree.left)
        if self.flag:
            self.pHead = pRootOfTree
            self.pre = pRootOfTree
            self.flag = False
        else:
            pRootOfTree.left = self.pre
            self.pre.right = pRootOfTree
            self.pre = pRootOfTree
        self.Convert(pRootOfTree.right)
        
        return self.pHead

7 二叉树的深度

题目描述

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

解题思路:这题跟二叉树和为某一值的路径思路是一样的,只不过原来是记录值,现在是记录路径的长度.

class Solution:
    def __init__(self):
        self.m = 0
    def maxDepth(self, pRoot,depth):
        if not pRoot:
            return 
        depth = depth+1
        if pRoot.left == None and pRoot.right == None:
            if self.m < depth:
                self.m = depth
        self.maxDepth(pRoot.left,depth)
        self.maxDepth(pRoot.right,depth)

    def TreeDepth(self, pRoot):
        if not pRoot:
            return 0
        self.maxDepth(pRoot,0)
        return self.m

8 平衡二叉树

题目描述

输入一棵二叉树,判断该二叉树是否是平衡二叉树。

平衡二叉树:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1

解题思路:递归,分解的小问题的解是,左数的高度与右树的高度之差不能大于1.求数的高度就是上一个题目.这里求数的深度用的是递归,更加简洁,一个节点的深度为max(左,右)+1,当为空时,返回的是0.

class Solution:
    def IsBalanced_Solution(self, pRoot):
        if pRoot == None :
            return True
        left = self.getdepth(pRoot.left)
        right = self.getdepth(pRoot.right)
        if abs(left - right) > 1:
            return False
        return self.IsBalanced_Solution(pRoot.left) and self.IsBalanced_Solution(pRoot.right)
    
    def getdepth(self, pRoot):
        if pRoot==None:
            return 0
        nleft = self.getdepth(pRoot.left)
        nright = self.getdepth(pRoot.right)
        return max(nleft,nright)+1

9 二叉树的下一个节点

题目描述

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

解题思路:最开始看到这题会觉着先中序遍历,找到给定的那个节点,然后再下一个节点就是了.看到代码以后,才知道,只给定了这么一个节点,用这个节点来表示它下一个节点.没办法,只能分情况.因为中序顺序是左-->根-->右.

1、有右子树,这时只需要把其右孩子作为下一个遍历的(并不是要找的)节点,然后沿着该节点的左子树(如果有的话)出发,直到遇到叶子节点,那么该叶子节点就是其下一个要找的节点;2、没有右子树,则判断该节点是否是其父节点的左孩子,如果是则其下一个要找的节点是其父节点;3、如果不是其父节点的左孩子,则把其父节点作为下一个遍历的节点,向上回溯,直到找到父节点没有父节点并且父节点是父节点的父节点的左孩子为止。

10 对称的二叉树

题目描述

请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

解题思路:对称的二叉树则左子树的左孩子值等于右子树的右孩子值,左子树的右孩子值等于右子树的左孩子值.所以这是一道递归题.

class Solution:
    def isSym(self, p1,p2):
        if not p1 and not p2:
            return True
        elif p1 and p2 and p1.val==p2.val:
            return self.isSym(p1.left,p2.right) and self.isSym(p1.right,p2.left)
        else:
            return False
    def isSymmetrical(self, pRoot):
        if not pRoot or not pRoot.left and not pRoot.right:
            return True
        elif pRoot.left.val == pRoot.right.val and pRoot.left and pRoot.right:
            return self.isSym(pRoot.left,pRoot.right)
        else:
            return False

11 按之字形顺序打印二叉树

题目描述

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

解题思路:之前前序中序后序访问用的都是深度优先,这个是广度优先的题目.广度优先利用的技巧就是堆栈/队列+while.按之子行打印,则需要记录一下行好.因为是按之打印,所以最好需要知道行是否访问结束.我的解决办法是定义2个变量,第一个变量存储这一行,当这一行访问完后,再访问下一行,用2个while语句.

class Solution:
    def Print(self, pRoot):
        if not pRoot:
            return []
        t1 = []
        t1.append(pRoot)
        t2 = []
        r1 = []
        r = []
        while(t1 or t2):
            cur = t1[0]
            del(t1[0])
            if cur:
                r1.append(cur.val)
                t2.append( cur.left)
                t2.append(cur.right)
            if not t1: 
                if r1:
                    r.append(r1)
                r1 = []
                while(t2):
                    cur = t2[0]
                    del(t2[0])
                    if cur:
                        r1.append(cur.val)
                        t1.append(cur.left)
                        t1.append(cur.right)
                r2 = []
                for j in range(len(r1)):  
                    r2.append(r1[-j-1]) 
                if r2:
                    r.append(r2)
                r1 = []                
        return r

12 把二叉树打印成多行

题目描述

从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

解题思路:这题和11一样,更简单一些,不用反转,直接copy代码.

class Solution:
    # 返回二维列表[[1,2],[4,5]]
    def Print(self, pRoot):
        if not pRoot:
            return []
        t1 = []
        t2 = []
        t1.append(pRoot)
        r1 = []
        r = []
        while(t1 or t2):
            cur = t1[0]
            del(t1[0])
            if cur:
                r1.append(cur.val)
                t2.append(cur.left)
                t2.append(cur.right)
            if not t1:
                if r1:
                    r.append(r1)
                r1 = []
                while(t2):
                    cur = t2[0]
                    del(t2[0])
                    if cur:
                        r1.append(cur.val)
                        t1.append(cur.left)
                        t1.append(cur.right)
                if r1:
                    r.append(r1)
                r1 = []
        return r

13 序列化二叉树

题目描述

请实现两个函数,分别用来序列化和反序列化二叉树.

序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存.反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树.

解题思路:序列化就按前序的顺序,遇到空的也需要标记.

class Solution:
    def Serialize(self, root):
        if not root:
            return []
        ss = []
        def preorder(root):
            if root == None:
                ss.append('#')
                return
            ss.append(root.val)
            preorder(root.left)
            preorder(root.right)
        preorder(root)
        return ss 
        # write code here
    def Deserialize(self, s):
        if not s:
            return None
        def build(ss):
            if not ss:
                return
            if ss[0] == '#':
                del(ss[0])
                return None
            tree = TreeNode(int(ss[0]))
            del(ss[0])
            tree.left = build(ss)
            tree.right = build(ss)
            return tree

        return build(s)

14 二叉搜索树的第k个节点

题目描述

给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8)    中,按结点数值大小顺序第三小结点的值为4。

解题思路:二叉搜索树中序遍历就是排序好的,再返回第k个节点就行.

class Solution:
    # 返回对应节点TreeNode
    def __init__(self):
        self.v = []
    def KthNode(self, pRoot, k):
        if not pRoot or k<=0:
            return None
        self.KthNode(pRoot.left, k)
        self.v.append(pRoot)
        self.KthNode(pRoot.right, k)
        
        if k > len(self.v):
            return None
        return self.v[k-1]
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值