Day 16 二叉树:最大/最小深度, 222. 完全二叉树节点数, 114. 展开为链表

104. 二叉树的最大深度

  • 思路
    • example
    • 节点A深度:根节点到节点A的长度,根节点深度记为1。(自上而下,以根节点为基准往下看。)
      • 最大深度: 从根节点出发往下走能走到的最深位置的长度 (根节点到叶子节点的最长路径)
      • 最小深度: 从根节点出发往下走能走到的最浅位置的长度 (根节点到叶子节点的最短路径)
        • 叶子节点:左右子树都为空。
    • 节点A高度:以哪个叶子节点为基准?
    • 递归
      • 后序遍历(自下而上,后处理当前节点):本质是求某种意义的高度
        • 树的最大深度为3: max(节点9为根节点的子树的最大深度=1, 节点20为根节点的子树的最大深度=2) + 1
      • 前序遍历(自上而下,先处理当前节点):直接求当前节点的深度,维护全局变量更新最大深度。需要“回溯”。
  • 复杂度. 时间:O(n), 空间: O(n)
  • 后序 (更好理解)自下而上 高度角度
class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        def traversal(root): 
            if root == None:
                return 0
            leftDepth = traversal(root.left) # 以root.left为根节点的深度
            rightDepth = traversal(root.right) # 以root.right为根节点的深度
            return max(leftDepth, rightDepth) + 1 # 以root为根节点的深度
        return traversal(root)
class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if root == None:
            return 0
        left = self.maxDepth(root.left)
        right = self.maxDepth(root.right)
        return max(left, right) + 1
  • 前序 自上而下,深度角度
class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        def dfs(root, depth):
            nonlocal res
            if root == None:
                return 
            res = max(res, depth) 
            dfs(root.left, depth + 1)
            dfs(root.right, depth + 1)
            return 
        res = 0
        dfs(root, 1)
        return res
  • BFS: 层序遍历 (迭代)
class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if root == None:
            return 0
        level = 0
        que = collections.deque([root])
        while que:
            size = len(que)
            level += 1
            for _ in range(size):
                node = que.popleft()
                if node.left:
                    que.append(node.left)
                if node.right:
                    que.append(node.right)
        return level 

111. 二叉树的最小深度

image
  • 思路
    • example
    • 递归,后序遍历。注意节点的左右子树为空才算叶子节点。
      • 最大深度:只要取左右子树的最大的深度即可。
      • 最小深度:
        • 左空右空,说明深度取决于当前节点(空与否)。
        • 左空右非空:取右子树最小深度。
        • 左非空右空:取左子树最小深度。
        • 左非空右非空:取左右最小深度 + 1 即可。
  • 复杂度. 时间:O(n), 空间: O(n)
  • 后序
class Solution:
    def minDepth(self, root: Optional[TreeNode]) -> int:
        def traversal(root):
            if root == None:
                return 0
            if root.left == None and root.right == None:
                return 1
            left_min, right_min = float('inf'), float('inf')
            if root.left:
                left_min = traversal(root.left)
            if root.right: 
                right_min = traversal(root.right)
            return min(left_min, right_min) + 1 
        return traversal(root)  
class Solution:
    def minDepth(self, root: Optional[TreeNode]) -> int:
        if root == None:
            return 0 
        if root.left == None and root.right == None:
            return 1
        left, right = float('inf'), float('inf')
        if root.left:
            left = self.minDepth(root.left)
        if root.right:
            right = self.minDepth(root.right)
        return min(left, right) + 1

-- Common Mistake

class Solution:
    def minDepth(self, root: Optional[TreeNode]) -> int:
        if root == None:
            return 0 
        left = self.minDepth(root.left)
        right = self.minDepth(root.right)
        return min(left, right) + 1
  • 前序
code
  • BFS: 层序遍历 (迭代)
class Solution:
    def minDepth(self, root: Optional[TreeNode]) -> int:
        if root == None:
            return 0
        res = 0
        que = collections.deque([root])
        while que:
            size = len(que)
            res += 1
            for _ in range(size):
                node = que.popleft()
                if node.left == None and node.right == None:
                    return res   
                if node.left: 
                    que.append(node.left)
                if node.right:
                    que.append(node.right)

222. 完全二叉树的节点个数

  • 思路
    • example
    • 在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。
    • 暴力法:递归后序遍历(自底向上)所有节点累计节点为个数即可。
      • 时间复杂度高,没有用到“完全二叉树”的性质。
  • 复杂度. 时间:O(n), 空间: O(\log (n))
class Solution:
    def countNodes(self, root: Optional[TreeNode]) -> int:
        def traversal(root):
            if root == None:
                return 0
            left_num = traversal(root.left)
            right_num = traversal(root.right)
            return left_num + right_num + 1
        return traversal(root)
  • 优化:利用完全二叉树和满二叉树的性质
  • 满二叉树节点数:2^{深度}-1 (根节点深度记为1
  • 对当前节点,深度遍历一路向左得到“左遍历”最大深度,同理深度遍历一路向右得到“右遍历”最大深度。(自上而下)
    • 如果两个深度相同,说明当前节点所在的子树为满二叉树(基于完全二叉树的假设)。
    • 如果两个深度不相同,刚其中一个为满二叉树,另一个非满。不管怎样,递归调用函数计算得到两个子树的节点数,相加再加1即可。
  • 时间:O(\log(n) \log(n))?
    • TBA
class Solution:
    def countNodes(self, root: Optional[TreeNode]) -> int:
        if root == None:
            return 0
        leftNode, rightNode = root.left, root.right
        leftDepth, rightDepth = 1, 1
        while leftNode: # 一路向左,左子树深度
            leftDepth += 1
            leftNode = leftNode.left 
        while rightNode: # 一路向右,右子树深度
            rightDepth += 1
            rightNode = rightNode.right 
        if leftDepth == rightDepth: # 满二叉树
            return 2**leftDepth - 1
        return self.countNodes(root.left) + self.countNodes(root.right) + 1 # 递归调用
class Solution:
    def countNodes(self, root: Optional[TreeNode]) -> int:
        if root == None:
            return 0 
        cur = root
        left_depth = 1
        while cur.left:
            left_depth += 1
            cur = cur.left 
        cur = root 
        right_depth = 1 
        while cur.right:
            right_depth += 1
            cur = cur.right 
        if left_depth == right_depth:
            return 2**left_depth - 1
        return self.countNodes(root.left) + self.countNodes(root.right) + 1  

114. Flatten Binary Tree to Linked List

  • 思路
    • example
    • 遍历解决 (前序) - 需要额外空间
class Solution:
    def flatten(self, root: TreeNode) -> None:
        preorderList = list()

        def preorderTraversal(root: TreeNode):
            if root:
                preorderList.append(root)
                preorderTraversal(root.left)
                preorderTraversal(root.right)
        
        preorderTraversal(root)
        size = len(preorderList)
        for i in range(1, size):
            prev, curr = preorderList[i - 1], preorderList[i]
            prev.left = None
            prev.right = curr
  • 分解子问题 (后序遍历)


class Solution:
    def flatten(self, root: Optional[TreeNode]) -> None:
        """
        Do not return anything, modify root in-place instead.
        """
        if root == None:
            return 
        self.flatten(root.left)
        self.flatten(root.right)
        new_left = root.left 
        new_right = root.right 
        root.left = None 
        root.right = new_left
        cur = root # 避免讨论corner case (new_left是否为None)
        while cur.right:
            cur = cur.right 
        cur.right = new_right 
class Solution:
    def flatten(self, root: Optional[TreeNode]) -> None:
        """
        Do not return anything, modify root in-place instead.
        """
        if root == None:
            return 
        self.flatten(root.left)
        self.flatten(root.right) 
        old_left = root.left 
        old_right = root.right 
        root.left = None 
        root.right = old_left 
        while root.right:
            root = root.right 
        root.right = old_right 
class Solution:
    def flatten(self, root: Optional[TreeNode]) -> None:
        """
        Do not return anything, modify root in-place instead.
        """
        if root == None:
            return 
        self.flatten(root.left)
        self.flatten(root.right) 
        old_left, old_right = root.left, root.right 
        root.right = old_left 
        root.left = None 
        cur = root
        while cur.right:
            cur = cur.right 
        cur.right = old_right 
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容