数据结构算法刷题(16)二叉树纲领篇

二叉树解题的思维模式分两类:

1、是否可以通过遍历一遍二叉树得到答案?如果可以,用一个traverse函数配合外部变量来实现,这叫「遍历」的思维模式。

2、是否可以定义一个递归函数,通过子问题(子树)的答案推导出原问题的答案?如果可以,写出这个递归函数的定义,并充分利用这个函数的返回值,这叫「分解问题」的思维模式。

3、前中后序是遍历二叉树过程中处理每一个节点的三个特殊时间点

前序位置的代码在刚刚进入一个二叉树节点的时候执行;

后序位置的代码在将要离开一个二叉树节点的时候执行;

中序位置的代码在一个二叉树节点左子树都遍历完,即将开始遍历右子树的时候执行。

二叉树的所有问题,就是让你在前中后序位置注入巧妙的代码逻辑,去达到自己的目的,你只需要单独思考每一个节点应该做什么,其他的不用你管,抛给二叉树遍历框架,递归会在所有节点上做相同的操作

二叉树遍历框架:

 

一旦你发现题目和子树有关,那大概率要给函数设置合理的定义和返回值,在后序位置写代码了

例题:

我们可以根据返回每一个节点的左右子树的最长路径,求得节点的最长路径

class TreeNode:

    def __init__(self, val=0, left=None, right=None):

        self.val = val

        self.left = left

        self.right = right

class Solution:

    def maxDepth(self, root: Optional[TreeNode]) -> int:

        if root is None: #如果根为空,返回0

            return 0

        leftmax = self.maxDepth(root.left) #利用定义,计算左右子树的最大深度

        rightmax = self.maxDepth(root.right)

        ret = max(leftmax,rightmax) + 1 #整棵树的最大深度等于左右子树的最大深度取最大值+1

        return ret

二叉查找树,又被称为二叉搜索树。其特点如下:设x为二叉查找树中的一个结点,x节点包含关键字key,一句话就是左孩子比父节点小,右孩子比父节点大,还有一个特性就是”中序遍历“可以让结点有序。

 

  • 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 任意节点的左、右子树也分别为二叉查找树;
  • 没有键值相等的节点。

在题目中:

  • 二叉搜索树,性质:左树节点值 < 根节点值 < 右树节点值
  • 如果root.val < low,那么左树节点值更小,要被剪枝,递归返回trimBST,从root.right开始
  • 如果root.val > high,那么右树节点值更大,要被剪枝,递归返回trimBST,从root.left开始
  • 若果root.val符合范围,那么就对root.left函数操作,对root.right执行函数操作,最终返回root即可

 

class Solution:

    def trimBST(self, root: Optional[TreeNode], low: int, high: int) -> Optional[TreeNode]:

        if root is None:

            return

        if root.val < low:

            return self.trimBST(root.right,low,high)

        if root.val > high:

            return self.trimBST(root.left,low,high)

        root.left = self.trimBST(root.left,low,high)

        root.right = self.trimBST(root.right,low,high)

        return root

 

 每一条二叉树的「直径」长度,就是一个节点的左右子树的最大深度之和

所以遍历一次二叉树,计算左右子树的最大深度和。

class Solution:

    ret = 0 #深度和

    def maxTree(self,root):

        if root is None:

            return 0

        leftmax = self.maxTree(root.left)

        rightmax = self.maxTree(root.right)

        self.ret = max(leftmax+rightmax,self.ret) #更新最大和

        anw = max(leftmax,rightmax) + 1 #递归的返回值

        return anw

    def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:

        self.maxTree(root)

        return self.ret

首先我们判断两棵树的节点值是否一样,然后再递归判断左子树是否一样,右子树是否一样,当p,q有一个是空的时候,退出递归,如果pq相等就是True否则就是False

class Solution:

    def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:

        if p is None or q is None:

            return p is q

        left_tree1 = self.isSameTree(p.left,q.left)

        right_tree1 = self.isSameTree(p.right,q.right)

        root_tree1 = True if p.val == q.val else False

        return left_tree1 and right_tree1 and root_tree1

 

思想:是否轴对称就是看左右子树是否对称

class Solution:

    def isSame(self,q,p):

        if q is None or p is None:

            return p is q

        left_tree = self.isSame(q.right,p.left)

        right_tree = self.isSame(p.right,q.left)

        same = True if q.val == p.val else False

        return left_tree and right_tree and same

    def isSymmetric(self, root: Optional[TreeNode]) -> bool:

        if root is None:

            return True

        return self.isSame(root.left,root.right)

 

首先我们需要计算左右子树的高度,如果高度差大于1的时候,将高度设置为-1返回,并且每一个接口都不做改变,一直返回到递归入口。然后再调用该函数,如果函数的值是-1那就是不平衡,如果函数值不是-1,就是平衡

class Solution:

    def getheight(self,root):

        if root is None:

            return 0

        left_high = self.getheight(root.left)

        if left_high == -1:

            return -1

        right_high = self.getheight(root.right)

        if right_high == -1 or abs(right_high-left_high) > 1:

            return -1

        return max(left_high,right_high)+1

    def isBalanced(self, root: Optional[TreeNode]) -> bool:

        height = self.getheight(root)

        return True if height > -1 else False

 

我们先遍历右子树,使用一个变量来记录当前右视图中的内容,如果层数(depth)等于当前记录的长度,就把该节点记录进去。然后再遍历左子树。

class Solution:

    def rightSideView(self, root: Optional[TreeNode]) -> List[int]:

        ans = [] #存放右视图答案

        def f(node,depth):

            if node is None:

                return 

            if depth == len(ans):

                ans.append(node.val) #前序遍历,先放答案再遍历

            f(node.right,depth+1) #右视图,先遍历右子树

            f(node.left,depth+1)

        f(root,0)

        return ans

这道题同样是要求子树的最大和,我们用一个变量存储当前总的最大和:

 

class Solution:

    def maxPathSum(self, root: Optional[TreeNode]) -> int:

        ans = -inf

        def f(node):

            if node is None:

                return 0

            left_sum = f(node.left)

            right_sum = f(node.right)

            nonlocal ans

            ans = max(left_sum + right_sum + node.val,ans)

            return max(0,max(left_sum,right_sum) + node.val) #因为当前加上该节点可能是负数,所以还要和0比较一下

        f(root)

        return ans

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值