剑指offer(python)--树

重建二叉树

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

思路:
  在二叉树的前序遍历序列中,第一个数字总是树的根结点的值。但在中序遍历序列中,根结点的值在序列的中间,左子树的结点的值位于根结点的值的左边,而右子树的结点的值位于根结点的值的右边。因此我们需要扫描中序遍历序列,才能找到根结点的值。

如下图所示,前序遍历序列的第一个数字1就是根结点的值。扫描中序遍历序列,就能确定根结点的值的位置。根据中序遍历特点,在根结点的值1前面的3个数字都是左子树结点的值,位于1后面的数字都是右子树结点的值。

  同样,在前序遍历的序列中,根结点后面的3个数字就是3个左子树结点的值,再后面的所有数字都是右子树结点的值。这样我们就在前序遍历和中序遍历两个序列中,分别找到了左右子树对应的子序列。

既然我们已经分别找到了左、右子树的前序遍历序列和中序遍历序列,我们可以用同样的方法分别去构建左右子树。也就是说,接下来的事情可以用递归的方法去完成

问题:如何表示出来?https://blog.csdn.net/weixin_38339143/article/details/79897670

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None
        
class Solution:
    # 返回构造的TreeNode根节点
    def reConstructBinaryTree(self, pre, tin):
        # write code here
        if len(pre)==0 or len(tin)==0:
            return None
        root_data = TreeNode(pre[0])#将根定义成节点的形式
        i=tin.index(pre[0])#i=tin.index(root_data.val)#寻找位置将左右子树分开 #返回根节点的索引
        root_data.left = self.reConstructBinaryTree(pre[1:1+i],tin[:i])
        root_data.right = self.reConstructBinaryTree(pre[1+i:],tin[i+1:])
        return root_data

# 实例化
if __name__=='__main__':
    pre=[1,2,4,7,3,5,6,8]
    tin=[4,7,2,1,5,3,8,6]
    demo = Solution()
    print(demo.reConstructBinaryTree(pre,tin))

二叉树的下一个节点

题目描述
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
对于这道题,要求找到中序遍历的下一个节点。

首先,对于中序遍历而言,自然是先左、再根、最后右。
中序是DNH BIEJ A FKVCLGM
在这里插入图片描述

分析二叉树的下一个节点,一共有以下情况:
1.二叉树为空,则返回空;
2.节点右孩子存在,则设置一个指针从该节点的右孩子出发,一直沿着指向左子结点的指针找到的叶子节点即为下一个节点F;(eg:D,B,E,A,C,G)
2、没有右子树的,也可以分成两类,a)是父节点左孩子(eg:N,I,L) ,那么父节点就是下一个节点 ; b)是父节点的右孩子(eg:H,J,K,M)找他的父节点的父节点的父节点…直到当前结点是其父节点的左孩子位置。如果没有eg:M,那么他就是尾节点

# -*- coding:utf-8 -*-
# class TreeLinkNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
#         self.next = None  #指向父节点的指针
class Solution:
    def GetNext(self, pNode):
        if pNode.right:#有右子树
            p=pNode.right
            while p.left:
                p=p.left
            return p
        while pNode.next:#无右子树,则找第一个当前节点是父节点左孩子的节点
            if(pNode.next.left==pNode):
                return pNode.next
            pNode = pNode.next#沿着父节点向上遍历
        return  #到了根节点仍没找到,则返回空

方法2:

思路,如果这道题是求中序遍历,肯定很简单。所以我们先写一个中序遍历的算法。关键是从根节点开始遍历,所以第一步还是找到某个节点的根节点,方法是一直使用next判断即可。
再将从根节点中序遍历的结果保存到一个数组中,直接找pNode所在索引的下一个即可。当然要考虑这个节点是不是最后一个,如果是最后一个,直接返回None。

链接:https://www.nowcoder.com/questionTerminal/9023a0c988684a53960365b889ceaf5e?f=discussion
来源:牛客网

class Solution:
    def GetNext(self, pNode):
        # write code here
        dummy = pNode
        while dummy.next:
            dummy = dummy.next
        self.result = []
        self.midTraversal(dummy)
        return self.result[self.result.index(pNode) + 1] if self.result.index(pNode) != len(self.result) - 1 else None
 
    def midTraversal(self, root):
        if not root: return
        self.midTraversal(root.left)
        self.result.append(root)
        self.midTraversal(root.right)

对称的二叉树

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

/*思路:首先根节点以及其左右子树,左子树的左子树和右子树的右子树相同

  • 左子树的右子树和右子树的左子树相同即可,采用递归比较左右节点,然后对左右节点的左右分支进一步递归比较
  • 非递归也可,采用栈或队列存取各级子树根节点
递归方法
# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def isSymmetrical(self, pRoot):
        if pRoot is None:
            return True
        return self.Traversal(pRoot.left,pRoot.right)
    def Traversal(self,left, right):
        if left is None and right is None:
            return True
        elif left and right and left.val ==right.val:
            return self.Traversal(left.left,right.right) and self.Traversal(left.right,right.left)
        else:
            return False

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

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

可以用一个队列,一个栈实现 也可用两个栈实现,我用两个栈实现,思路简单一点。
在这里插入图片描述

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def Print(self, pRoot):
        if pRoot is  None:
            return []
        queue=[]
        queue.append(pRoot)#把根节点存储在队列
        result=[]#存储树有多少层,1,2,3,4之类
        while len(queue)!=0:
            res=[] #存储每层节点
            nextStack=[] #存储左右子树节点
            for i in queue:
                res.append(i.val)
                if i.left:
                    nextStack.append(i.left)
                if i.right:
                    nextStack.append(i.right)
            queue=nextStack
            result.append(res) ##这里就是数组里面套数组
        returnResult=[]
        for i, v in enumerate(result):
            if i%2==0:
                returnResult.append(v)
            else:
                returnResult.append(v[::-1])#这里每一个里面是一个数组
                #result=[[1],[2,3],[4,5,6,7]]
        return returnResult
            

把二叉树打印成多行

题目描述
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
思路: 二叉树的层次遍历 先计算出根节点的左右子树,借助一个队列,再循环递归
思路还是很清晰的,使用两个队列一个存放节点,一个存放值。先将根节点加入到队列中,然后遍历队列中的元素,遍历过程中,访问该元素的左右节点,再将左右子节点加入到队列中来

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def Print(self, pRoot):
        if pRoot is  None:
            return []
        queue=[]
        queue.append(pRoot)#把根节点存储在队列
        result=[]#存储树有多少层,1,2,3,4之类
        while len(queue)!=0:
            res=[] #存储每层节点
            nextStack=[] #存储左右子树节点
            for i in queue:
                res.append(i.val)
                if i.left:
                    nextStack.append(i.left)
                if i.right:
                    nextStack.append(i.right)
            queue=nextStack
            result.append(res) #这里得到是每一层,和每一层对应的节点
        return result

          

序列化二叉树

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

对于序列化:使用前序遍历,递归的将二叉树的值转化为字符,并且在每次二叉树的结点 不为空时,在转化val所得的字符之后添加一个’ ,'作为分割。对于空节点则以 ‘#’ 代替。
2. 对于反序列化:按照前序顺序,递归的使用字符串中的字符创建一个二叉树(特别注意: 在递归时,递归函数的参数一定要是char ** ,这样才能保证每次递归后指向字符串的指针会 随着递归的进行而移动!!!)

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def __init__(self):
        self.flag = -1
         
    def Serialize(self, root):
        # write code here
        if not root:
            return '#,'
        return str(root.val)+','+self.Serialize(root.left)+self.Serialize(root.right)
         
    def Deserialize(self, s):
        # write code here
        self.flag += 1
        l = s.split(',')
         
        if self.flag >= len(s):
            return None
        root = None
         
        if l[self.flag] != '#':
            root = TreeNode(int(l[self.flag]))
            root.left = self.Deserialize(s)
            root.right = self.Deserialize(s)
        return root

判断结果

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

思路:

  1. 二叉树的后序遍历 先左节点再是右节点,最后是根节点
    2.二叉搜索树是对一个有序数组进行二分查找形成的搜索树,它指一棵空树或者具有下列性质的二叉树:
    若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值; 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
    任意节点的左、右子树也分别为二叉查找树;

以下图二叉树为例,我们都知道对二叉搜索树进行中序遍历可以还原为一个有序数组,而图中二叉树后序遍历结果为[3,2,4,6,8,7,5]。接下来分析一下这个数组的规律。首先按照后序遍历的性质,数组的最后一个元素即是整个二叉树的根结点。另外按照搜索二叉树的前两条性质,左子树均小于根,右子树均大于根。左右子树部分同样符合整个规律,所以我们可以递归实现数组的判断。
在这里插入图片描述

# -*- coding:utf-8 -*-
class Solution:
     def VerifySquenceOfBST(self, sequence):
        # write code here
        if len(sequence) == 0:
            return False
        else:
            root = sequence[-1]
            del sequence[-1]
            lefttree = []
            righttree =[]
            # 左子树和右子树分界
            splitindex = -1
            for i in range(len(sequence)):
                # 值小于根结点的归为左子树
                if sequence[i] < root:
                    lefttree.append(sequence[i])
                    splitindex = i
                else:
                    break
            for i in range(splitindex+1, len(sequence)):
                # 若右子树部分有小于根结点的值,说明不是二叉搜索树
                if sequence[i] > root:
                    righttree.append(sequence[i])
                else:
                    return False
            if len(lefttree) <= 1:
                left = True
            else:
                # 递归判断左子树
                left = self.VerifySquenceOfBST(lefttree)
            if len(righttree) <= 1:
                right = True
            else:
                right = self.VerifySquenceOfBST(righttree)
            return left and right

二叉搜索树的第k个节点

给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
思路分析:
进行深度遍历–然后存到列表里,

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回对应节点TreeNode
    def KthNode(self, pRoot, k):
        self.res=[]
        self.dfs(pRoot)
        if 0<k<=len(self.res):
            return self.res[k-1] 
        else:
            return None
    def dfs(self,root):
        if not root:return
        self.dfs(root.left)
        self.res.append(root)
        self.dfs(root.right)

树的子结构

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

正确思路 遍历二叉树A,定位B的根节点在A中的可能位置——> 定位后,验证B是不是A当前位置的子结构。**
本题的思路在于子结构的判断,首先需要判断两棵树的根节点是否相同,若是不同则递归调用其左子树和B树,若仍不同则递归调用其右子树和B树,若都不同则返回false。
在这里插入图片描述
原文:https://blog.csdn.net/u010005281/article/details/79460325 --完整测试代码

1.先在A中找和B的根节点相同的结点

2.找到之后遍历对应位置的其他结点,直到B中结点遍历完,都相同时,则B是A的子树

3.对应位置的结点不相同时,退出继续在A中寻找和B的根节点相同的结点,重复步骤,直到有任何一棵二叉树为空退出

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def HasSubtree(self, pRoot1, pRoot2):
        if pRoot1==None or pRoot2==None:
            return False
        result=False
        if pRoot1.val==pRoot2.val:
            result=self.isSubtree(pRoot1,pRoot2)
        if result==False:
            result=self.HasSubtree(pRoot1.left,pRoot2)|self.HasSubtree(pRoot1.right,pRoot2)
        return result
    def isSubtree(self,root1,root2):
        #该函数判断在找到和子树根节点相同的节点之后,判断其余节点是否相同
        if root2==None:
            return True
        if root1==None:
            return False
        if root1.val==root2.val:
            return self.isSubtree(root1.left,root2.left)& self.isSubtree(root1.right,root2.right)
        return False
        

是否是平衡二叉树

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

思路:平衡二叉树的定义是任何节点的左右子树高度差都不超过1的二叉树。

按照定义,很容易得出获得左右子树高度,然后判断。递归写法。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def IsBalanced_Solution(self, pRoot):
        if not pRoot: return True
        left = self.TreeDepth(pRoot.left)
        right = self.TreeDepth(pRoot.right)
        if abs(left - right) > 1:
            return False
        return self.IsBalanced_Solution(pRoot.left) and self.IsBalanced_Solution(pRoot.right)

    def TreeDepth(self, pRoot):
        if not pRoot: return 0
        left = self.TreeDepth(pRoot.left)
        right = self.TreeDepth(pRoot.right)
        return max(left, right) + 1

对比:求二叉树的深度(Python代码)

def TreeDeep(self, pRoot):
    if not pRoot:
        return 0
    left = self.TreeDeep(pRoot.left)
    right = self.TreeDeep(pRoot.right)
    return left+1 if left>right else right+1

二叉树深度和平衡二叉树

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

求树的深度,可以从层次遍历出发考虑 层次遍历可以使用队列完成,也可以使用递归完成,所以有两种方法

方法一:使用队列
class Solution:
    # 层次遍历
    def levelOrder(self, root):
        # write your code here
        # 存储最后层次遍历的结果
        res = []
        # 层数
        count = 0
        # 如果根节点为空,则返回空列表
        if root is None:
            return count
        # 模拟一个队列储存节点
        q = []
        # 首先将根节点入队
        q.append(root)
        # 列表为空时,循环终止
        while len(q) != 0:
            # 使用列表存储同层节点
            tmp = []
            # 记录同层节点的个数
            length = len(q)
            for i in range(length):
                # 将同层节点依次出队
                r = q.pop(0)
                if r.left is not None:
                    # 非空左孩子入队
                    q.append(r.left)
                if r.right is not None:
                    # 非空右孩子入队
                    q.append(r.right)
                tmp.append(r.val)
            if tmp:
                count += 1  # 统计层数
            res.append(tmp)
        return count
 
    def TreeDepth(self, pRoot):
        # write code here
        # 使用层次遍历
        # 当树为空直接返回0
        if pRoot is None:
            return 0
        count = self.levelOrder(pRoot)
        return count
方法二:使用递归方法
class Solution:
    def TreeDepth(self, pRoot):
        # write code here
        # 使用层次遍历
        # 当树为空直接返回0
        if pRoot is None:
            return 0
        # 方法2:使用递归
        # 如果该树只有一个结点,它的深度为1.如果根节点只有左子树没有右子树,
        # 那么树的深度为左子树的深度加1;同样,如果只有右子树没有左子树,
        # 那么树的深度为右子树的深度加1。如果既有左子树也有右子树,
        # 那该树的深度就是左子树和右子树的最大值加1.
        count = max(self.TreeDepth(pRoot.left), self.TreeDepth(pRoot.right)) + 1
        return count

二叉树和为某一值的路径

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

not x 意思相当于 if x is false, then True, else False

代码中经常会有变量是否为None的判断,有三种主要的写法:

第一种是if x is None;

第二种是 if not x:;

第三种是if not x is None(这句这样理解更清晰if not (x is None))

思路1:–递归

首先要理解题意,是从根节点往子节点连。

1、如果只有根节点或者找到叶子节点,我们就把其对应的val值返回

2、如果不是叶子节点,我们分别对根节点的左子树、右子树进行递归,直到找到叶子结点。然后遍历把叶子结点和父节点对应的val组成的序列返回上一层;如果没找到路径,其实也返回了序列,只不过是[]

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def FindPath(self, root,target_number):
        result =[]
        if not root:
            return result
        if not root.left and not root.right and root.val == target_number:
            return [[root.val]]
        else:
            left = self.FindPath(root.left,target_number - root.val)
            right = self.FindPath(root.right,target_number - root.val)
            for item in left+right:
                result.append([root.val]+item)
            return result

思路2:和思路1类似的想法,但是提出了一些python的注意事项

这是一道DFS题目,也可以看做是先序遍历的题目 ,在二叉树中,dfs就相当于先序遍历
首先,采用一种“减法”思想,当检查一棵树从根到叶子节点形成的路径的和是否为target时,先将当前根节点的值 root.val
加入path, 然后检查它的左子树(若非空),看从左子树的根到叶子节点形成的路径的和是否为 target - root.val (递归),
然后同样的道理去递归检查右子树(若非空),这便是大致的思路。 但这道题麻烦的一点是,它要求记录下所有符合标准的路径,这便用到了dfs的特性。
但又来了一件麻烦事,先序遍历便是先左后右。检查完左子树后,会对path就行修改,再去查找右子树,如何将path恢复到之前未进行左子树检查的状态?

一开始自己的做法是,每到分叉路口,到对当前路径做一份拷贝,用原路径去继续进行左子树的递归,拷贝路径进行右子树的递归,这样左子树对右子树的结果产生影响,但是这样造成了大量的拷贝,浪费空间
比较好的做法是将path设为全局的,然后dfs的过程便是先序遍历的过程,一旦遍历到叶子结点,便将path最后的节点移除掉,这样在递归一层一层进行的时候将值添加进path,在递归返回的过程中将path最末尾的元素一个一个移除。这样便依靠递归的特性完成了路径的恢复。

例如对于树 10,5,12,5,7,#,#,#,#,#,#,#,#(层序遍历),path变化过程为 10,5,5 》》 10,5 》》
10,5,7 》》10,5 》》10 》》10,12 》》10 》》 null

因为最终遍历到叶子结点时,若发现符合规定的路径,是把path加入到结果集中,因为java中添加的是引用,而path是不断变化的,所以我们要新new一个当前路径的副本出来,防止添加进去的path都是相同的(最终path的状态)。

当然,这道题也可以用加法的思想,看dfs过程中能否加出target

因为题意没有说节点值全为正数,所以必须递归到根节点才能确定这条路径能否加出target,而不能到中间节点就加到>=target了,就认为这条路径不行了,假如这条路径后序有0或者负数的情况,还是能加出target的。
经验教训 深刻理解DFS的用法 深刻理解递归是如何恢复path的,从先序遍历进行考虑,递归是如何一步步进行的,如何一步步返回的
最终添加入结果集中,必须添加的是path的副本 递归中,全局变量的使用

# -*- coding:utf-8 -*-
class Solution:
    # 返回二维列表,内部每个列表表示找到的路径
    def FindPath(self, root, expectNumber):
        # write code here
        if not root:
            return []
 
        ret = []
        path = []
        self.Find(root, expectNumber, ret, path)
        # print ret
        # a = [];self.f(a);print a
        return ret
     
    def Find(self, root, target, ret, path):
        if not root:
            return
 
        path.append(root.val)
        isLeaf = (root.left is None and root.right is None)
        if isLeaf and target == root.val:
            ret.append(path[:])  # 这里这一步要千万注意啊,
            # 假如是:ret.append(path), 结果是错的。因为Python可变对象都是引用传递啊。
 
        #print "target:", target, "isLeaf:", isLeaf,
        #print "val:", root.val, "path:", path, "ret:", ret
        #print
 
        if root.left:
            self.Find(root.left, target - root.val, ret, path)
        if root.right:
            self.Find(root.right, target - root.val, ret, path)
 
        path.pop()

二叉树镜像

题目描述
操作给定的二叉树,将其变换为源二叉树的镜像。
输入描述:
二叉树的镜像定义:源二叉树
(https://img-blog.csdnimg.cn/20190221215648202.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI0NDI5MzMz,size_16,color_FFFFFF,t_70)
思路:
将左子树和右子树交换
递归思想,先交换根节点的左右子树的位置,然后向下递归,把左右子树的根节点作为下次循环的根节点

class Solution:
    # 返回镜像树的根节点
    def Mirror(self, root):
        if root==None:
            return 
        if root.left==None and root.right==None:
            return 
       #这三步交换根节点的左右树,交换的树,而不单单是节点
        tmp=root.right
        root.right=root.left
        root.left=tmp
        if root.left is not None:
            self.Mirror(root.left)
        if root.right is not None:
            self.Mirror(root.right)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值