Day 16 二叉樹:最大/最小深度, 222. 完全二叉樹節(jié)點(diǎn)數(shù), 114. 展開為鏈表

104. 二叉樹的最大深度

  • 思路
    • example
    • 節(jié)點(diǎn)A深度:根節(jié)點(diǎn)到節(jié)點(diǎn)A的長度,根節(jié)點(diǎn)深度記為1。(自上而下,以根節(jié)點(diǎn)為基準(zhǔn)往下看。)
      • 最大深度: 從根節(jié)點(diǎn)出發(fā)往下走能走到的最深位置的長度 (根節(jié)點(diǎn)到葉子節(jié)點(diǎn)的最長路徑)
      • 最小深度: 從根節(jié)點(diǎn)出發(fā)往下走能走到的最淺位置的長度 (根節(jié)點(diǎn)到葉子節(jié)點(diǎn)的最短路徑)
        • 葉子節(jié)點(diǎn):左右子樹都為空。
    • 節(jié)點(diǎn)A高度:以哪個葉子節(jié)點(diǎn)為基準(zhǔn)?
    • 遞歸
      • 后序遍歷(自下而上,后處理當(dāng)前節(jié)點(diǎn)):本質(zhì)是求某種意義的高度。
        • 樹的最大深度為3: max(節(jié)點(diǎn)9為根節(jié)點(diǎn)的子樹的最大深度=1, 節(jié)點(diǎn)20為根節(jié)點(diǎn)的子樹的最大深度=2) + 1
      • 前序遍歷(自上而下,先處理當(dāng)前節(jié)點(diǎn)):直接求當(dāng)前節(jié)點(diǎn)的深度,維護(hù)全局變量更新最大深度。需要“回溯”。
  • 復(fù)雜度. 時間: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為根節(jié)點(diǎn)的深度
            rightDepth = traversal(root.right) # 以root.right為根節(jié)點(diǎn)的深度
            return max(leftDepth, rightDepth) + 1 # 以root為根節(jié)點(diǎn)的深度
        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
    • 遞歸,后序遍歷。注意節(jié)點(diǎn)的左右子樹為空才算葉子節(jié)點(diǎn)。
      • 最大深度:只要取左右子樹的最大的深度即可。
      • 最小深度:
        • 左空右空,說明深度取決于當(dāng)前節(jié)點(diǎn)(空與否)。
        • 左空右非空:取右子樹最小深度。
        • 左非空右空:取左子樹最小深度。
        • 左非空右非空:取左右最小深度 + 1 即可。
  • 復(fù)雜度. 時間: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. 完全二叉樹的節(jié)點(diǎn)個數(shù)

  • 思路
    • example
    • 在完全二叉樹中,除了最底層節(jié)點(diǎn)可能沒填滿外,其余每層節(jié)點(diǎn)數(shù)都達(dá)到最大值,并且最下面一層的節(jié)點(diǎn)都集中在該層最左邊的若干位置。
    • 暴力法:遞歸后序遍歷(自底向上)所有節(jié)點(diǎn)累計節(jié)點(diǎn)為個數(shù)即可。
      • 時間復(fù)雜度高,沒有用到“完全二叉樹”的性質(zhì)。
  • 復(fù)雜度. 時間: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)
  • 優(yōu)化:利用完全二叉樹和滿二叉樹的性質(zhì)。
  • 滿二叉樹節(jié)點(diǎn)數(shù):2^{深度}-1 (根節(jié)點(diǎn)深度記為1
  • 對當(dāng)前節(jié)點(diǎn),深度遍歷一路向左得到“左遍歷”最大深度,同理深度遍歷一路向右得到“右遍歷”最大深度。(自上而下)
    • 如果兩個深度相同,說明當(dāng)前節(jié)點(diǎn)所在的子樹為滿二叉樹(基于完全二叉樹的假設(shè))。
    • 如果兩個深度不相同,剛其中一個為滿二叉樹,另一個非滿。不管怎樣,遞歸調(diào)用函數(shù)計算得到兩個子樹的節(jié)點(diǎn)數(shù),相加再加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 # 遞歸調(diào)用
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 
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容