数据结构算法刷题(24)二叉树

一、深度优先遍历

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:

            return 0

        l_depth = self.maxDepth(root.left)

        r_depth = self.maxDepth(root.right)

        return max(l_depth,r_depth)+1

 另一种思路:维护一个全局变量,不在遍历时返回,在遍历时直接更新全局变量

class Solution:

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

        ans = 0

        def f(node,cnt):

            if node is None:

                return

            cnt += 1

            nonlocal ans

            ans = max(ans,cnt)

            f(node.left,cnt)

            f(node.right,cnt)

        f(root,0)

        return ans

 思路:对于最小深度有两种特殊情况:如果root是空,那最小深度是0,如果root没有左右子树,那最小深度是1.假设该树有左子树,那计算叶子节点的最小深度。假设该树有右子树,就计算右子树的最小深度。用全局变量存储最小深度,每次查看是否比当前值小再更新。

class Solution:

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

        if root is None:  #如果root是空,那最小深度是0

            return 0 

        if root.left is None and root.right is None: #如果root没有左右子树,那最小深度是1

            return 1

        mindepth = inf

        if root.left: #用全局变量存储最小深度,每次查看是否比当前值小再更新。

            mindepth = min(self.minDepth(root.left),mindepth)

        if root.right:

            mindepth = min(self.minDepth(root.right),mindepth)

        return mindepth + 1

思路:从根节点开始,用目标值减去当前的val如果一直到叶子节点,目标值与当前叶子的val相等了,则存在这个路径。两个边界条件:(1)root是空的时候,返回FALSE(2)当root的左右子树都是空的时候,判断现在root的val和目标值是否相等。递归条件:传入左右子树和目标值减去当前的val。

class Solution:

    def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:

        if root is None:

            return False

        if root.left is None and root.right is None:

            return targetSum == root.val

        return self.hasPathSum(root.left, targetSum-root.val)  or  self.hasPathSum (root.right, targetSum - root.val)

class Solution:

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

        ans = []

        path = []

        def f(root,targetSum):

            nonlocal ans,path

            if root is None: #边界条件

                return

            path.append(root.val)

            if root.left is None and root.right is None and root.val == targetSum:

                    ans.append(path.copy() #到达叶子节点,找到路径

            f(root.left,targetSum-root.val)

            f(root.right,targetSum-root.val)

            path.pop() #回溯的时候把当前值pop掉

        f(root,targetSum)

        return ans

 思路二:直接用递归,每次的路径也是递归过程中传递下去的

class Solution:

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

        ans = []

        def f(root,targetSum,path):

            nonlocal ans

            if root is None:

                return

            if root.left is None and root.right is None and root.val == targetSum:

                    ans.append(path+[root.val])

            f(root.left,targetSum-root.val,path+[root.val])

            f(root.right,targetSum-root.val,path+[root.val])

        f(root,targetSum,[])

        return ans

思路:每个节点都对应一个数字,等于其父节点对应的数字乘以10再加上该节点的值(这里假设根节点的父节点对应的数字是 0)。只要计算出每个叶子节点对应的数字,然后计算所有叶子节点对应的数字之和,即可得到结果。

class Solution:

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

        #计算出每个叶子节点的值,相加就是全部数字之和。

        def f(root,s):

            if root is None:

                return 0

            s = s*10 + root.val

            if root.left is None and root.right is None:

                return s

            return f(root.left,s) + f(root.right,s)

        return f(root,0)

 

思路:用递归,传路径。

class Solution:

    def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:

        ans = []

        def f(root,path):

            nonlocal ans

            if root is None:

                return

            if root.left is None and root.right is None:

                ans.append(path+str(root.val))

            f(root.left,path+str(root.val)+'->')

            f(root.right,path+str(root.val)+'->')

        f(root,'')

        return ans

 思路:边界条件:如果遍历到p和q某一个为空了,就不会继续遍历了,如果p is q,那就返回true,否则返回FALSE。然后递归p和q的值是否相同,以及两颗树的左子树,和两棵树的右子树。

class Solution:

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

        if q is None or p is None:

            return p is q

        return q.val == p.val and self.isSameTree(p.left,q.left) and self.isSameTree(p.right,q.right)

思路:看根节点的左右子树是否轴对称,那就是q的左子树应该等于p的右子树。 

class Solution:

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

        def f(p,q):

            if p is None or q is None:

                return p is q

            return p.val == q.val and f(p.left,q.right) and f(p.right,q.left)

        return f(root.left,root.right)

思路:我们去计算两个子树的高度,然后通过高度差来判断是否平衡。如果不平衡,就让高度差为-1,然后不断返回-1到递归的入口。最后判断拿到的值是否是-1,不是就返回true,否则就返回FALSE。

class Solution:

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

        def get_height(node):

            if node is None:

                return 0

            left_height = get_height(node.left)

            if left_height == -1:

                return -1

            right_height = get_height(node.right)

            if right_height == -1 or abs(right_height-left_height) > 1:

                return -1

            return max(left_height,right_height) + 1 #树的高度

        return get_height(root) != -1

思路:维护一个ans,先递归右子树,当ans的长度小于该节点的的高度时,就把答案写进去,如果大于就不写,然后再递归左子树。 

class Solution:

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

        ans = []

        def f(root,depth):

            if root is None:

                return

            if depth == len(ans): #增加答案

                ans.append(root.val)

            f(root.right,depth+1)

            f(root.left,depth+1)

        f(root,0)

        return ans

思路: #递归左右子树调换,直到为叶子节点或者为空

class Solution:

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

        if root is None or (root.left is None and root.right is None):

            return root

        t = root.left

        root.left = root.right

        root.right = t

        self.invertTree(root.left)

        self.invertTree(root.right)

        return root

思路一:计算在该路径下的祖先的最大值和最小值。然后用该值和当前节点的差值以及记录的值作比较,取最大。当前最大值和最小值随节点变动,在递归时传入。

class Solution:

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

        ans = 0

        def f(root,min_num,max_mun):

            nonlocal ans

            if root is None:

                return 

            min_num = min(root.val,min_num)

            max_mun = max(root.val,max_mun)

            ans = max(ans,root.val - min_num,max_mun - root.val)

            f(root.left,min_num,max_mun)

            f(root.right,min_num,max_mun)

        f(root,root.val,root.val)

        return ans

 思路二:从根节点的角度来说,我只需要记录这条路径下的最大值和最小值,在递归回根节点的时候,计算差值即可。

class Solution:

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

        ans = 0

        def f(root,min_mun,max_num):

            nonlocal ans

            if root is None:

                ans = max(ans,max_num - min_mun)

                return

            max_num = max(root.val,max_num)

            min_mun = min(root.val,min_mun)

            f(root.left,min_mun,max_num)

            f(root.right,min_mun,max_num)

        f(root,root.val,root.val)

        return ans

思路:

 

class Solution:

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

        ans = 0

        def f(node,l,r):

            nonlocal ans

            if node is None:

                return

            ans = max(ans,l,r) #维护此时的最大值

            if node.left: f(node.left,r+1,0) #往左子树走,左值是父亲节点的r+1,右值是0

            if node.right: f(node.right,0,l+1)

        f(root,0,0)

        return ans

class Solution:

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

        limit -= root.val

        if root.left is None and root.right is None: #叶子节点

            return None if limit > 0 else root

        if root.left: root.left = self.sufficientSubset(root.left,limit)

        if root.right: root.right = self.sufficientSubset(root.right,limit)

        return root if root.left or root.right else None

class Solution:

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

        ans = []

        def f(root,to_delete):

            nonlocal ans

            if root is None:

                return None

#后序遍历,在删根节点的时候,已经可以拿到左右子树了。

            root.left = f(root.left,to_delete) #更新左儿子为递归左子树之后的结果

            root.right = f(root.right,to_delete) #更新右儿子为递归右子树之后的结果

            if root.val not in to_delete: #如果该点不在列表里,不需要删除

                return root

            if root.left: ans.append(root.left) #需要删除该点时,有左子树,左子树入队

            if root.right:ans.append(root.right) #有右子树,右子树入队

            return None #最后将该点置位空返回

        if f(root,to_delete): ans.append(root) #在调用根节点之后,如果不是空,就把根节点入队

        return ans

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值