leetcode-樹(一)

目錄

104. 二叉樹的最大深度

111. 二叉樹的最小深度

226. 翻轉二叉樹

100. 相同的樹

102. 二叉樹的層序遍歷

101. 對稱二叉樹

222. 完全二叉樹的節點個數

110. 平衡二叉樹

112. 路徑總和

404. 左葉子之和


104. 二叉樹的最大深度

https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/

給定一個二叉樹,找出其最大深度。二叉樹的深度爲根節點到最遠葉子節點的最長路徑上的節點數。說明: 葉子節點是指沒有子節點的節點。

示例:給定二叉樹 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
返回它的最大深度 3 。

題解

一:maxDepth返回以root爲根的二叉樹的最大深度。

class Solution(object):
    def maxDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        if not root:
            return 0

        return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1
class Solution(object):
    def maxDepth(self, root):
        if not root:
            return 0
        return self._max_depth(root)

    # return:以root爲根的樹的最大深度
    def _max_depth(self, root):
        if not root:
            return 0
        return max(self._max_depth(root.left), self._max_depth(root.right)) + 1

111. 二叉樹的最小深度

https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/

給定一個二叉樹,找出其最小深度。最小深度是從根節點到最近葉子節點的最短路徑上的節點數量。說明: 葉子節點是指沒有子節點的節點。

示例:給定二叉樹 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
返回它的最小深度  2.

題解

minDepth返回以root爲根的最小深度,注意與104題的區別,這邊要注意例如下面這種情況,樹的最大深度是2,最小深度也是2,若依錯誤的寫法,最小深度則是1,因爲將1的右子樹傳入,則返回0,因此最終結果是1,但是我們發現1的左子樹還是有值的,故不可這麼做,那麼若左子樹爲空,得看右子樹;同理右子樹爲空得看左子樹;都不爲空,二者取最小:

[1,2]

    1
   / 
  2  

class Solution(object):
    def minDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        if not root:
            return 0
        # if not root.left and not root.right:
        #     return 1
        elif not root.left:
            return self.minDepth(root.right) + 1
        elif not root.right:
            return self.minDepth(root.left) + 1
        else:
            return min(self.minDepth(root.left), self.minDepth(root.right)) + 1

下面這種解法是錯誤的

class Solution(object):
    def minDepth(self, root):
        if not root:
            return 0
        return min(self.minDepth(root.left), self.minDepth(root.right)) + 1

226. 翻轉二叉樹

https://leetcode-cn.com/problems/invert-binary-tree/

翻轉一棵二叉樹。

示例:

輸入:

     4
   /   \
  2     7
 / \   / \
1   3 6   9
輸出:

     4
   /   \
  7     2
 / \   / \
9   6 3   1

題解

一:invertTree返回翻轉後的二叉樹的根(翻轉以root爲根的二叉樹,並將新的二叉樹的根返回 )。

class Solution(object):
    def invertTree(self, root):
        """
        :type root: TreeNode
        :rtype: TreeNode
        """
        if not root:
            return root
        left = self.invertTree(root.right)
        right = self.invertTree(root.left)
        # 此時若有一個子樹爲空,另一個不爲空,例如左子樹有值,右子樹爲空
        # 翻轉後,將root的左節點重新指向翻轉後的右子樹(None)
        # 翻轉後,將root的右節點重新指向翻轉後的左子樹
        root.left, root.right = left, right
        return root

100. 相同的樹

https://leetcode-cn.com/problems/same-tree/

給定兩個二叉樹,編寫一個函數來檢驗它們是否相同。如果兩個樹在結構上相同,並且節點具有相同的值,則認爲它們是相同的。

示例 1:

輸入:       1         1
               / \       / \
             2   3    2   3

        [1,2,3],   [1,2,3]

輸出: true
示例 2:

輸入:      1          1
               /           \
             2             2

        [1,2],     [1,null,2]

輸出: false
示例 3:

輸入:       1         1
               / \       / \
             2   1     1   2

        [1,2,1],   [1,1,2]

輸出: false

題解

一:isSameTree返回以p和q爲根的子樹是否相同。

class Solution(object):
    def isSameTree(self, p, q):
        """
        :type p: TreeNode
        :type q: TreeNode
        :rtype: bool
        """
        if not p and not q:
            return True
        if (not p and q) or (not q and p) or p.val != q.val:
            return False
        return self.isSameTree(p.left, q.left) and self.isSameTree(q.right, p.right)   

102. 二叉樹的層序遍歷

https://leetcode-cn.com/problems/binary-tree-level-order-traversal/

給你一個二叉樹,請你返回其按 層序遍歷 得到的節點值。 (即逐層地,從左到右訪問所有節點)。

示例:二叉樹:[3,9,20,null,null,15,7],

      3
     /  \
   9  20
       /  \
     15   7
返回其層次遍歷結果:

[
  [3],
  [9,20],
  [15,7]
]

題解

一:層序遍歷。雙端隊列,此處應該先進先出,故每次向隊尾(append)添加元素,從隊首(popleft)移除元素。

from collections import deque
class Solution(object):
    def levelOrder(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        if not root:
            return []
        q, res = deque(), []
        q.append(root)
        while q:
            n = len(q)
            res.append([])
            for i in range(n):
                node = q.popleft()
                res[-1].append(node.val)
                if node.left:
                    q.append(node.left)
                if node.right:
                    q.append(node.right)
        return res
from Queue import Queue
class Solution(object):
    def levelOrder(self, root):
        res = []
        if not root:
            return res
        rec = Queue()
        rec.put(root)
        
        while not rec.empty():
            tmp, n = [], rec.qsize()
            while n > 0:
                node = rec.get()
                if node.left:
                    rec.put(node.left)
                if node.right:
                    rec.put(node.right)
                tmp.append(node.val)
                n -= 1
            res.append(tmp[:])
        return res

 

101. 對稱二叉樹

https://leetcode-cn.com/problems/symmetric-tree/

給定一個二叉樹,檢查它是否是鏡像對稱的。

例如,二叉樹 [1,2,2,3,4,4,3] 是對稱的。

      1
    /   \
   2    2
  / \    / \
3  4  4  3
但是下面這個 [1,2,2,null,3,null,3] 則不是鏡像對稱的:

    1
   /  \
  2   2
   \    \
   3    3
進階:你可以運用遞歸和迭代兩種方法解決這個問題嗎?

題解

一:遞歸,以root爲根的二叉樹是否對稱。轉化成兩個子樹是否對稱,即它們的兩個根結點具有相同的值,每個樹的右子樹都與另一個樹的左子樹鏡像對稱。時間複雜度:O(n),因爲我們遍歷整個輸入樹一次,所以總的運行時間爲 O(n),其中 n是樹中結點的總數。

class Solution(object):
    # 以root爲根的二叉樹是否對稱
    # 左子樹的右孩子=右子樹的左孩子
    # 左子樹的左孩子=右子樹的右孩子
    def isSymmetric(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        if not root:
            return True
        return self._isSymmetric(root.left, root.right)
    # 以left和right爲根的二叉樹是否對稱
    def _isSymmetric(self, left, right):
        if not left and not right:
            return True 
        if (not left and right) or (not right and left) or (left.val != right.val):
            return False
        return (self._isSymmetric(left.right, right.left) and self._isSymmetric(left.left, right.right))

二:借鑑102題層序遍歷的思路,每次取出一層之後看是否是鏡像,當然下層中的None也要記錄,不過下層爲None,就沒必要入隊了,因爲一旦上層形狀給定,下層的形狀(記錄過None的)也唯一給定。

from collections import deque
class Solution(object):
    def isSymmetric(self, root):
        if not root or (not root.left and not root.right):
            return True
        if (not root.left and root.right) or (root.left and not root.right):
            return False
        q = deque()
        q.append(root)

        while q:
            n = len(q)
            rec = []
            for i in range(n):
                node = q.popleft()
                if node.left:
                    q.append(node.left)
                    rec.append(node.left.val)
                else:
                    rec.append(None)
                if node.right:
                    q.append(node.right)
                    rec.append(node.right.val)
                else:
                    rec.append(None)
            # print(rec)
            l, r = 0, len(rec) - 1
            while l < r:
                if rec[l] != rec[r]:
                    return False
                l += 1
                r -= 1
        return True

三:copy官方題解,https://leetcode-cn.com/problems/symmetric-tree/solution/dui-cheng-er-cha-shu-by-leetcode/,除了遞歸的方法外,我們也可以利用隊列進行迭代。隊列中每兩個連續的結點應該是相等的,而且它們的子樹互爲鏡像。最初,隊列中包含的是 root的左子樹 以及 root的右子樹。該算法的工作原理類似於 BFS,但存在一些關鍵差異。每次提取兩個結點並比較它們的值。然後,將兩個結點的左右子結點按相反的順序插入隊列中。當隊列爲空時,或者我們檢測到樹不對稱(即從隊列中取出兩個不相等的連續結點)時,該算法結束。

from collections import deque
class Solution(object):
    def isSymmetric(self, root):
        if not root or (not root.left and not root.right):
            return True
        if (not root.left and root.right) or (root.left and not root.right):
            return False
        q = deque()
        q.append(root.left)
        q.append(root.right)

        while q:
            l, r = q.popleft(), q.popleft()
            if not l and not r:
                continue
            if (not l and r) or (l and not r) or (l.val != r.val):
                return False
            q.append(l.left)
            q.append(r.right)
            q.append(l.right) 
            q.append(r.left)       
        return True

222. 完全二叉樹的節點個數

給出一個完全二叉樹,求出該樹的節點個數。說明:完全二叉樹的定義如下:在完全二叉樹中,除了最底層節點可能沒填滿外,其餘每層節點數都達到最大值,並且最下面一層的節點都集中在該層最左邊的若干位置。若最底層爲第 h 層,則該層包含 1~ 2h 個節點。

示例:

輸入: 
    1
   / \
  2   3
 / \  /
4  5 6

輸出: 6

題解

一:暴力求解,將每個節點都遍歷一遍,O(n)的時間複雜度。

class Solution(object):
    def countNodes(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        if not root:
            return 0
        return self.countNodes(root.left) + self.countNodes(root.right) + 1

二:借鑑官方題解https://leetcode-cn.com/problems/count-complete-tree-nodes/solution/wan-quan-er-cha-shu-de-jie-dian-ge-shu-by-leetcode/,求出樹的最大深度d,因爲完全二叉樹,則非葉子 節點的個數2^{(d-1)}-1,然後關鍵是求出葉子節點的個數。

現在有兩個問題:最後一層我們需要檢查多少個節點?一次檢查的最佳的時間性能是什麼?
讓我們從第一個問題開始思考。最後一層的葉子節點全部靠向左邊,我們可以用二分搜索只檢查 log(2^{(d-1)}) = d-1個葉子代替檢查全部葉子。

讓我們思考第二個問題,最後一層的葉子節點索引在[0,2^{(d - 1)} - 1]之間。如何檢查第 idx 節點是否存在?讓我們來用二分搜索來構造從根節點到 idx 的移動序列。如,idx = 4。idx 位於 0,1,2,3,4,5,6,7 的後半部分,因此第一步是向右移動;然後 idx 位於 4,5,6,7 的前半部分,因此第二部是向左移動;idx 位於 4,5 的前半部分,因此下一步是向左移動。一次檢查的時間複雜度爲 O(d)。我們需要 O(d)次檢查,一次檢查需要O(d),所以總的時間複雜度爲O(d^2)

在這裏插入圖片描述

class Solution(object):
    # 深度 + 葉子節點的個數
    def countNodes(self, root):
        if not root:
            return 0
            
        d = self.depth(root)
        if d == 1:
            return 1

        non_leaf_nodes = 2 ** (d - 1) - 1

        l, r = 0, 2 ** (d - 1) - 1
        while l <= r:
            pivot = l + (r - l) // 2
            if self.exists(pivot , d, root):
                l = pivot + 1
            else:
                r = pivot - 1
        return non_leaf_nodes + l
 
    def depth(self, node):
        d = 1
        while node.left:
            d += 1
            node = node.left
        return d
    
    def exists(self, idx, d, node):
        l, r = 0, 2 ** (d - 1) - 1
        for _ in range(d - 1):
            pivot = l + (r - l) // 2
            if idx <= pivot:
                node = node.left
                r = pivot
            else:
                node = node.right
                l = pivot + 1
        return node is not None

110. 平衡二叉樹

https://leetcode-cn.com/problems/balanced-binary-tree/

給定一個二叉樹,判斷它是否是高度平衡的二叉樹。本題中,一棵高度平衡二叉樹定義爲:一個二叉樹每個節點 的左右兩個子樹的高度差的絕對值不超過1。

示例 1:

給定二叉樹 [3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7
返回 true 。

示例 2:

給定二叉樹 [1,2,2,3,3,null,null,4,4]

       1
      / \
     2   2
    / \
   3   3
  / \
 4   4
返回 false 。
題解

一:暴力解法,遍歷二叉樹中的每個節點,查看該節點的左右子樹的最大深度相差是否不超過1。若近乎平衡時間複雜度O(nlgn),對於每個深度爲 d 的節點 p,_maxDepth被調用d(即lg(n))次。如果樹是傾斜的,算法沒有儘早結束,最終會達到 O(n^2)的複雜度。

class Solution(object):
    def isBalanced(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        if not root:
            return True
        if abs(self._maxDepth(root.left) - self._maxDepth(root.right)) > 1:
            return False
        return self.isBalanced(root.left) and self.isBalanced(root.right)
        
    def _maxDepth(self, node):
        if not node:
            return 0
        return max(self._maxDepth(node.left), self._maxDepth(node.right)) + 1

二:提前終止,時間複雜度爲 O(N)。

class Solution(object):
    def isBalanced(self, root):
        if not root:
            return True
        return self._helper(root) != -1
        
    def _helper(self, node):
        if not node:
            return 0
        left_depth = self._helper(node.left)
        if left_depth == -1:
            return -1
        right_depth = self._helper(node.right)
        if right_depth == -1:
            return -1
        if abs(left_depth - right_depth) <= 1:
            return max(left_depth, right_depth) + 1
        return -1

112. 路徑總和

https://leetcode-cn.com/problems/path-sum/

給定一個二叉樹和一個目標和,判斷該樹中是否存在根節點到葉子節點的路徑,這條路徑上所有節點值相加等於目標和。

說明: 葉子節點是指沒有子節點的節點。

示例: 
給定如下二叉樹,以及目標和 sum = 22,

              5
             / \
            4   8
           /   / \
          11  13  4
         /  \      \
        7    2      1
返回 true, 因爲存在目標和爲 22 的根節點到葉子節點的路徑 5->4->11->2。

題解

一:葉子節點判斷,要保證葉子節點的合理性,需要先判斷該節點是否爲空

class Solution(object):
    def hasPathSum(self, root, sum):
        """
        :type root: TreeNode
        :type sum: int
        :rtype: bool
        """
        if not root:
            return False
        if not root.left and not root.right and sum - root.val == 0:
            return True
        return self.hasPathSum(root.left, sum - root.val) or self.hasPathSum(root.right, sum - root.val)

404. 左葉子之和

https://leetcode-cn.com/problems/sum-of-left-leaves/

計算給定二叉樹的所有左葉子之和。

示例:

    3
   / \
  9  20
    /  \
   15   7

在這個二叉樹中,有兩個左葉子,分別是 9 和 15,所以返回 24

題解

一:純粹的遞歸。

class Solution(object):
    def sumOfLeftLeaves(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        if not root:
            return 0
        left_leaf = 0
        if root.left and not root.left.right and not root.left.left:
            left_leaf = root.left.val
        return self.sumOfLeftLeaves(root.left) + self.sumOfLeftLeaves(root.right) + left_leaf

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章