算法&数据结构(六):二叉树

leetcode:617. 合并二叉树

问题描述:给定两个二叉树,合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

解法:指定其中一个树,如果到达叶子节点,则下一步返回另一个树的结点(包含这个节点的左右子树)

时间复杂度:log(n)

class Solution(object):
    def mergeTrees(self, t1, t2):
        """
        :type t1: TreeNode
        :type t2: TreeNode
        :rtype: TreeNode
        """
        # 如果其中一个二叉树终止,则直接返回另一个
        if not t1:
            return t2
        if not  t2:
            return t1
        t1.val = t1.val + t2.val
        t1.left = self.mergeTrees(t1.left, t2.left)
        t1.right = self.mergeTrees(t1.right, t2.right)
        return t1
        

剑指offer:树的子结构

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

解法:首先找到树A中(与树B的根结点的值相同)的结点,判断是不是有相同结构,然后递归判断左右结点,递归的终止条件是到达了树A或者树B的叶结点

时间复杂度:log(n)

# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def HasSubtree(self, pRoot1, pRoot2):
        # 如果树A与树B任意一个为空,则肯定不存在子结构
        res = False
        # 找到树A中(与树B的根结点的值相同)的结点
        if pRoot1 and pRoot2:
            if pRoot1.val == pRoot1.val:
                res = self.check_structure(pRoot1, pRoot2)
            if res == False:
                res = self.HasSubtree(pRoot1.left, pRoot2)
            # 遍历完左子树,如果没有找到就继续遍历右子树
            if res == False:
                res = self.HasSubtree(pRoot1.right, pRoot2)
        return res
    
    # 找递归判断左右结点,递归的终止条件是到达了树A或者树B的叶结点
    def check_structure(self, root1, root2):
        # 到达了树B的叶结点
        if root2 is None:
            return True
        # 到达了树A的叶结点
        if root1 is None:
            return False
        # 树A与树B的当前结点的值不相同
        if root1.val != root2.val:
            return False
        left_check = self.check_structure(root1.left, root2.left)
        right_check = self.check_structure(root1.right, root2.right)
        return  left_check and right_check

剑指offer:二叉树的镜像

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

解法:先序遍历(根左右)树的每个节点,如果遍历到的结点有子结点,就交换它的两个子结点。当交换完所有非叶子结点的左右子结点之后,就得到了树的镜像

时间复杂度:log(n)

# -*- 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 root is None:
            return root
        root.left, root.right = root.right, root.left
        self.Mirror(root.left)
        self.Mirror(root.right)
        return root

剑指offer:重建二叉树

问题描述:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树

解法:根据前序遍历(根左右)找到根节点,中序遍历的根节点划分了左右子树。递归分别返回左、右子树的前序遍历和中序遍历

时间复杂度:n*log(n)

# -*- 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 len(pre) == 0:
            return None
        if len(pre) == 1:
            # 按照树节点的结构定义!!!
            return TreeNode(pre[0])
        # 找到根节点,按照树节点的结构定义!!!
        root = TreeNode(pre[0])
        # 在中序遍历中找到根节点,并且划分左\右子树
        root_p = tin.index(root.val)
        root.left = self.reConstructBinaryTree(pre[1:root_p+1], tin[:root_p])
        # 注意下标:tin[root_p+1:]
        root.right = self.reConstructBinaryTree(pre[root_p+1:], tin[root_p+1:])
        return root

剑指offer:二叉树的深度

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

解法:最长路径的深度=最长路径的叶结点数目,如果树只有一个结点,深度为1。如果有左右子树,则为左右的深度较大值加1。

时间复杂度:

# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def TreeDepth(self, pRoot):
        if pRoot is None:
            return 0
        l = self.TreeDepth(pRoot.left)
        r = self.TreeDepth(pRoot.right)
        return l+1 if l > r else r+1

剑指offer:平衡二叉树

问题描述:输入一棵二叉树,判断该二叉树是否是平衡二叉树。平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。

解法:后序遍历(左右根),遍历节点的时候记录它的深度

时间复杂度:O(n)

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 如果每个节点都是平衡的,最后返回的是深度
    def get_depth(self, root):
        if root is None:
            return 0
        left = self.get_depth(root.left)
        right = self.get_depth(root.right)
        # 如果某个子树不平衡,则返回-1
        if left == -1 or right == -1:
            return -1
        return max(left, right) + 1 if abs(right - left) < 2 else -1
    
    def IsBalanced_Solution(self, pRoot):
        return self.get_depth(pRoot) != -1

剑指offer:从上往下打印二叉树

问题描述:从上往下打印出二叉树的每个节点,同层节点从左至右打印。

解法:前序遍历(根左右),放入根节点,然后把它的左右节点放入队列。先入先出能够保证子树节点进入之后,同层的节点先打印。

时间复杂度:O(log(n))

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回从上到下每个节点值列表,例:[1,2,3]
    def PrintFromTopToBottom(self, root):
        # 异常值处理
        if root is None:
            return []
        queue = [root] 
        output = []
        
        while queue:
            if queue[0].left:
                queue.append(queue[0].left)
            if queue[0].right:
                queue.append(queue[0].right)
            # 根节点的值放入输出序列
            output.append(queue[0].val)
            # 队列头部取出根节点
            queue.pop(0)
        return output

剑指offer:二叉搜索树的后序遍历序列

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

解法:后序遍历,左右根。后序遍历序列的最后一个元素为根节点,序列可以分为左子树+右子树。如果在子树中发现与根节点大小关系不一致的节点,则返回错误。

二叉搜索树的性质:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

时间复杂度:O(log(n))

class Solution:
    def VerifySquenceOfBST(self, sequence):
        # 输入[] 
        if not sequence:
            return False
        if len(sequence) == 1:
            return True
        # !!! 当根节点只有一个左子树时,右子树的遍历会根据split指向的元素开始。如果初始化为0,那么右子树会错误地读入左子树
        split = len(sequence)-1
        # 找到根节点
        root = sequence[-1]
        # 找到划分点
        for _ in range(0, len(sequence)-1):
            if sequence[_] > root:
                split = _
                break
        # 确认后半段都比root大
        for _ in range(split, len(sequence)-1):
            if sequence[_] < root:
                return False

        # 递归检查前半段和后半段
        left = True
        # 前半段至少有两个元素,才递归
        if split > 0:
            left = self.VerifySquenceOfBST(sequence[:split])
        right = True
        # 后半段至少有两个元素,才递归
        if split < len(sequence) - 1:
            right = self.VerifySquenceOfBST(sequence[split:-1])
        return left and right

a = Solution()
print(a.VerifySquenceOfBST([]))

剑指offer:二叉树中和为某一值的路径

问题描述:输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径

解法:(当前输入值的更新=当前输入值-根节点值)递归遍历左右子树,返回所有满足的路径。

class Solution:
    # 返回二维列表,内部每个列表表示找到的路径
    def FindPath(self, root, expectNumber):
        if not root:
            return []
        # 叶子节点且等于输入值,返回当前节点
        if not root.left and not root.right and root.val == expectNumber:
            return [[root.val]]
        # 非叶子节点,遍历它的左右节点
        left = self.FindPath(root.left, expectNumber - root.val)
        right = self.FindPath(root.right, expectNumber - root.val)
        return [[root.val] + i for i in left+right]

剑指offer:二叉搜索树与双向链表

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

解法:中序遍历,把左子树的最右边一个结点返回,把右子树的最左边结点返回,然后根节点再连接这两个结点,形成双向链表

时间复杂度:

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def Convert(self, pRootOfTree):
        # 把左子树的最右边一个结点返回,把右子树的最左边结点返回,然后根节点再连接这两个结点
        if not pRootOfTree:
            return pRootOfTree
        if not pRootOfTree.left and not pRootOfTree.right:
            return pRootOfTree
        
        left = pRootOfTree.left
        # 遍历左子树
        self.Convert(left)
        # 找到左子树的最右边一个结点
        # 'NoneType' object has no attribute 'left'
        if left:
            while left.right:
                left = left.right
            # 连接根节点,形成双向链表
            # 'NoneType' object has no attribute 'left'  
            left.right, pRootOfTree.left = pRootOfTree, left
        
        right = pRootOfTree.right
        # 遍历右子树
        self.Convert(right)
        # 找到右子树的最左边结点
        if right:
            while right.left:
                right = right.left
            # 连接根节点,形成双向链表
            right.left, pRootOfTree.right = pRootOfTree, right
        
        # 返回最小值的节点,位于左子树的最左边
        head = pRootOfTree
        while head.left:
            head = head.left
        return head

leetcode:101. 对称二叉树

问题描述:给定一个二叉树,检查它是否是镜像对称的。例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

解法:镜像的条件为,它们的两个根结点具有相同的值,且每个树的右子树都与另一个树的左子树镜像对称

时间复杂度:

# 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:
        if not root:
            return True
        def balance(r1, r2):
            if r1 is None and r2 is None:
                return True
            if r1 is None or r2 is None:
                return False
            return r1.val == r2.val and balance(r1.left, r2.right) and balance(r1.right, r2.left)
        return balance(root.left, root.right)
    
        

leetcode:437. 路径总和 III

问题描述:给定一个二叉树,它的每个结点都存放着一个整数值。找出路径和等于给定数值的路径总数。路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

解法:保存从根节点到路径的和列表,每一步计算等于sum的个数。dfs递归时,之前的和依次加上当前节点的值,再添加当前节点。

class Solution(object):
    def pathSum(self, root, sum):
        """
        :type root: TreeNode
        :type sum: int
        :rtype: int
        """
        if not root:
            return 0
        # target 所有路径所能构成的和列表
        def dfs(node, target):
            if not node:
                return 0
            # 左右的值默认为0
            l, r = 0, 0
            # 之前的和依次加上当前节点的值,再添加当前节点
            target = [node.val] + [_ + node.val for _ in target]
            # 递归
            l = dfs(node.left, target)
            r = dfs(node.right, target)
            # 计算当前的路径和等于给定数值的个数
            return target.count(sum) + l + r
        return dfs(root, [])

剑指offer:543. 二叉树的直径

问题描述:给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过根结点。

解法:对于每一个当前节点,选择左、右节点中最大的长度,然后加上1(当前路径),则是当前的最长路径深度。全局变量dis为直径,每次取左、右节点深度的和。

class Solution(object):
    def __init__(self):
        self.dis = 0
    def diameterOfBinaryTree(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        if not root:
            return 0
        def diameter(node):
            if node is None:
                return 0
            l = diameter(node.left)
            r = diameter(node.right)
            self.dis = max(l+r, self.dis)
            # 选择左右节点中最大的长度,然后加上1(当前路径)
            return max(l,r) + 1
        diameter(root)
        return self.dis

leetcode:538. 把二叉搜索树转换为累加树

问题描述:给定一个二叉搜索树(Binary Search Tree),把它转换成为累加树(Greater Tree),使得每个节点的值是原来的节点值加上所有大于它的节点值之和。

解法:先遍历右子树,储存根节点,再遍历左子树

class Solution(object):
    def convertBST(self, root):
        # 从最右的节点开始,到根,到根的左节点
        self.num = 0
        def search(root):
            if not root:
                return 
            search(root.right)
            self.num += root.val
            root.val = self.num
            search(root.left)
            return root
        return search(root)

leetcode:

问题描述:

解法:

时间复杂度:

发布了93 篇原创文章 · 获赞 119 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_18310041/article/details/96332885