BFS和DFS之二叉树的最大宽度

LeetCode 662. 二叉树最大宽度

给定一个二叉树,编写一个函数来获取这个树的最大宽度。树的宽度是所有层中的最大宽度。这个二叉树与满二叉树(full binary tree)结构相同,但一些节点为空。

每一层的宽度被定义为两个端点(该层最左和最右的非空节点,两端点间的null节点也计入长度)之间的长度。

示例 1:

输入: 

           1
         /   \
        3     2
       / \     \  
      5   3     9 

输出: 4
解释: 最大值出现在树的第 3 层,宽度为 4 (5,3,null,9)

示例 2:

输入: 

          1
         /  
        3    
       / \       
      5   3     

输出: 2
解释: 最大值出现在树的第 3 层,宽度为 2 (5,3)

示例 3:

输入: 

          1
         / \
        3   2 
       /        
      5      

输出: 2
解释: 最大值出现在树的第 2 层,宽度为 2 (3,2)

示例 4:

输入: 

          1
         / \
        3   2
       /     \  
      5       9 
     /         \
    6           7
输出: 8
解释: 最大值出现在树的第 4 层,宽度为 8 (6,null,null,null,null,null,null,7)

思路
由于我们需要将给定树中的每个节点都访问一遍,我们需要遍历树。我们可以用深度优先搜索或者宽度优先搜索将树遍历。

这个问题中的主要想法是给每个节点一个 position 值,如果我们走向左子树,那么 position -> position x 2,如果我们走向右子树,那么 position -> positon x 2 + 1。当我们在看同一层深度的位置值 L 和 R 的时候,宽度就是 R - L + 1

BFS

宽度优先搜索顺序遍历每个节点的过程中,我们记录节点的 position 信息,对于每一个深度,第一个遇到的节点是最左边的节点最后一个到达的节点是最右边的节点

class Solution:
    def widthOfBinaryTree(self, root: TreeNode) -> int:
        stack = [(root, 1, 1)] # root在第一层第一个位置
        curr_level = 0 # 初始化层
        left = 0 # 初始化left
        ans = 0 # 初始化结果
        while stack:
            node, level, pos = stack.pop(0)
            if node:
                stack.append((node.left, level + 1, pos * 2)) # 左子树节点, 层数+1,位置为pos*2
                stack.append((node.right, level + 1, pos * 2 + 1)) # 右子树节点,层数+1,位置为pos*2+1
                if curr_level != level: # 进入新的一层,更新层数
                    curr_level = level 
                    left = pos # 到达新层的第一个节点位置赋给left,后续节点的curr_levle一直等于level,不需要更新left;直到到达新的下一层,再更新层数
                ans = max(pos - left + 1, ans) # 计算当前位置节点距离当前层最左侧的距离,更新结果
        return ans

复杂度分析

时间复杂度: O(N),其中 N 是输入树的节点数目,我们遍历每个节点一遍。

空间复杂度: O(N),这是stack 的大小。

DFS

按照深度优先的顺序,我们记录每个节点的 position 。对于每一个深度,第一个到达的位置会被记录在 left[depth] 中。

然后对于每一个节点,它对应这一层的可能宽度是 pos - left[depth] + 1 。我们将每一层这些可能的宽度去一个最大值就是答案。

class Solution:
    def widthOfBinaryTree(self, root: TreeNode) -> int:
        self.ans = 0
        self.left = {} # 记录每一层最先到达的节点位置
        self.dfs(root)
        return self.ans
    
    def dfs(self, node, depth=0, pos=1):
        if node:
            self.left.setdefault(depth, pos) # depth不在就添加,在的话,就不更新,相当于下面两行
            # if depth not in self.left:
            #    self.left[depth] = pos
            self.ans = max(self.ans, pos - self.left[depth] + 1)
            self.dfs(node.left, depth + 1, pos * 2)
            self.dfs(node.right, depth + 1, pos * 2 + 1)

复杂度分析

时间复杂度: O(N) ,其中 N 是树中节点的数目,我们需要遍历每个节点。

空间复杂度: O(N) ,这部分空间是因为我们 DFS 递归过程中有 N 层的栈。

setdefault() 方法
Python 字典 setdefault() 方法和 get() 方法类似,返回指定键的值,如果键不在字典中,将会添加键并将值设置为一个指定值,默认为None。

get() 和 setdefault() 区别: setdefault() 返回的键如果不在字典中,会添加键(更新字典),而 get() 不会添加键。

语法
setdefault() 方法语法:

D.setdefault(key[,default=None])
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值