[英雄星球六月集训LeetCode解题日报] 第15日 深度优先搜索

一、508. 出现次数最多的子树元素和

链接: 508. 出现次数最多的子树元素和

1. 题目描述

给你一个二叉树的根结点 root ,请返回出现次数最多的子树元素和。如果有多个元素出现的次数相同,返回所有出现次数最多的子树元素和(不限顺序)。

一个结点的 「子树元素和」 定义为以该结点为根的二叉树上所有结点的元素之和(包括结点本身)。

2. 思路分析

DFS模拟即可,用一个计数器记录每个和出现次数,最后输出最多的那些和。

3. 代码实现

class Solution:
    def findFrequentTreeSum(self, root: TreeNode) -> List[int]:
        cnt = collections.defaultdict(int)
        def dfs(root):
            if not root:
                return 0
            ans = root.val
            if root.left:
                ans += dfs(root.left)
            if root.right:
                ans += dfs(root.right)
            cnt[ans] += 1
            return ans
        
        dfs(root)
        m = max(cnt.values())
        ans = [k for k,v in cnt.items() if v == m]
        return ans

在这里插入图片描述

二、655. 输出二叉树

链接: 655. 输出二叉树

1. 题目描述

在一个 m*n 的二维字符串数组中输出二叉树,并遵守以下规则:

行数 m 应当等于给定二叉树的高度。
列数 n 应当总是奇数。
根节点的值(以字符串格式给出)应当放在可放置的第一行正中间。根节点所在的行与列会将剩余空间划分为两部分(左下部分和右下部分)。你应该将左子树输出在左下部分,右子树输出在右下部分。左下和右下部分应当有相同的大小。即使一个子树为空而另一个非空,你不需要为空的子树输出任何东西,但仍需要为另一个子树留出足够的空间。然而,如果两个子树都为空则不需要为它们留出任何空间。
每个未使用的空间应包含一个空的字符串""。
使用相同的规则输出子树。

2. 思路分析

由于空树也要输出"",因此实际是输出一个完全二叉树的位置。

  • 先计算深度,确定输出的矩阵有几行。
  • 宽度=2^深度-1
  • 对每层来说,节点应该在当前层管辖区域的中间位置。
  • 管辖区域的边界实际上父节点知道,就是父节点两侧。
  • 因此dfs传递层深和左右边界位置即可。
  • 根节点(第一层)管家一整行。

3. 代码实现

class Solution:
    def printTree(self, root: TreeNode) -> List[List[str]]:
        def find_depth(root):
            if not root:
                return 0
            return max(find_depth(root.left),find_depth(root.right))+1
        m = find_depth(root)
        n = 2**m -1
        ans = [['']*n for _ in range(m)]

        def dfs(root,depth,l,r):
            if not root:
                return
            mid = (l+r)//2
            ans[depth][mid] = str(root.val)
            dfs(root.left, depth+1,l,mid-1)
            dfs(root.right, depth+1,mid+1,r)

        dfs(root,0,0,n-1)
        return ans

三、 1026. 节点与其祖先之间的最大差值

链接: 1026. 节点与其祖先之间的最大差值

1. 题目描述

给定二叉树的根节点 root,找出存在于 不同 节点 A 和 B 之间的最大值 V,其中 V = |A.val - B.val|,且 A 是 B 的祖先。

(如果 A 的任何子节点之一为 B,或者 A 的任何子节点是 B 的祖先,那么我们认为 A 是 B 的祖先)

2. 思路分析

考虑一个数字和一个集合中任意数字的最大差值,一定是取集合中的最大值或最小值做差。

  • 那么dfs时传递当前长辈节点中的最大最小值即可。
  • 注意父节点没有,因此dfs入口可以从父节点的左右儿子进。

3. 代码实现

class Solution:
    def maxAncestorDiff(self, root: Optional[TreeNode]) -> int:
        ans = 0
        def dfs(root,_min,_max):
            if not root:
                return
            nonlocal ans
            ans = max([ans,abs(_min - root.val),abs(_max-root.val)])
            dfs(root.left,min(_min,root.val),max(_max,root.val))
            dfs(root.right,min(_min,root.val),max(_max,root.val))
           
        dfs(root.left,root.val,root.val)
        dfs(root.right,root.val,root.val)
        return ans

四、 1600. 王位继承顺序

链接: 1600. 王位继承顺序

1. 题目描述

一个王国里住着国王、他的孩子们、他的孙子们等等。每一个时间点,这个家庭里有人出生也有人死亡。

这个王国有一个明确规定的王位继承顺序,第一继承人总是国王自己。我们定义递归函数 Successor(x, curOrder) ,给定一个人 x 和当前的继承顺序,该函数返回 x 的下一继承人。

Successor(x, curOrder):
    如果 x 没有孩子或者所有 x 的孩子都在 curOrder 中:
        如果 x 是国王,那么返回 null
        否则,返回 Successor(x 的父亲, curOrder)
    否则,返回 x 不在 curOrder 中最年长的孩子

比方说,假设王国由国王,他的孩子 Alice 和 Bob (Alice 比 Bob 年长)和 Alice 的孩子 Jack 组成。

  • 一开始, curOrder 为 [“king”].
  • 调用 Successor(king, curOrder) ,返回 Alice ,所以我们将 Alice 放入 curOrder 中,得到 [“king”, “Alice”] 。
  • 调用 Successor(Alice, curOrder) ,返回 Jack ,所以我们将 Jack 放入 curOrder 中,得到 [“king”, “Alice”, “Jack”] 。
  • 调用 Successor(Jack, curOrder) ,返回 Bob ,所以我们将 Bob 放入 curOrder 中,得到 [“king”, “Alice”, “Jack”, “Bob”] 。
  • 调用 Successor(Bob, curOrder) ,返回 null 。最终得到继承顺序为 [“king”, “Alice”, “Jack”, “Bob”] 。
    通过以上的函数,我们总是能得到一个唯一的继承顺序。

请你实现 ThroneInheritance 类:

  • ThroneInheritance(string kingName) 初始化一个 ThroneInheritance 类的对象。国王的名字作为构造函数的参数传入。
  • void birth(string parentName, string childName) 表示 parentName 新拥有了一个名为 childName 的孩子。
  • void death(string name) 表示名为 name 的人死亡。一个人的死亡不会影响 Successor 函数,也不会影响当前的继承顺序。你可以只将这个人标记为死亡状态。
  • string[] getInheritanceOrder() 返回 除去 死亡人员的当前继承顺序列表。

2. 思路分析

读题读了半个小时。
最后发现就是一个树的前序遍历(先根遍历)。
用一个字典记录每个人的节点位置,便于生孩子。
输出时跳过死人即可。

3. 代码实现

class Node:
    def __init__(self, name: str):
        self.name = name
        self.children = []

class ThroneInheritance:

    def __init__(self, kingName: str):
        self.king = Node(kingName)
        self.person = {kingName:self.king}
        self.deaths = set()


    def birth(self, parentName: str, childName: str) -> None:
        child = Node(childName)
        self.person[childName] = child
        self.person[parentName].children.append(child)


    def death(self, name: str) -> None:
        self.deaths.add(self.person[name])

    def getInheritanceOrder(self) -> List[str]:
        deaths = self.deaths
        ans = []
        def dfs(king):
            if not king:
                return
            if king not in deaths:
                ans.append(king.name)
            for child in king.children:
                dfs(child)
        dfs(self.king)
        return ans

五、 124. 二叉树中的最大路径和

链接: 124. 二叉树中的最大路径和

1. 题目描述

路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。

路径和 是路径中各节点值的总和。

给你一个二叉树的根节点 root ,返回其 最大路径和 。

2. 思路分析

再来一题。
也算是树上dp,自己的状态从子树转移而来。
定义dp[cur]为以自己为祖宗节点的一条单边路径的最大权。
对于任意节点,自己加上左右两条单边路径(可不取),就能组成所有路径。
那么:
ans = max{dp[l]+dp[r]+val|dp[l]>0,dp[r]>0} 其中l,r是val这个节点的左右孩子

3. 代码实现

class Solution:
    def maxPathSum(self, root: Optional[TreeNode]) -> int:
        """
        dfs返回以本节点为祖宗节点的上升路径最大值。
        根据左右节点的dfs结果更新一下答案:
        左右节点+本节点一定可以组成一条路径,自己必须参加,左右节点选正数从参加。计算最大路径,更新答案
        自己返回的结果应该是左右两边大的+自己,如果都是负的就都不要,只要自己。
        """
        ans = -10**9

        def dfs(root):
            if not root:
                return 0
            m = root.val 
            left = dfs(root.left)
            right = dfs(root.right)
            if left > 0:
                m += left
            if right > 0:
                m += right
            nonlocal ans
            ans = max(ans,m)
            ret = root.val
            m = max(left,right)
            if m > 0:
                ret += m
            return ret
        dfs(root)
        return ans
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值