平衡二叉樹專題

力扣關於平衡二叉樹的題目仍是有一些的,而且都很是經典,推薦你們練習。今天給你們精選了 4 道題,若是你完全搞明白了這幾道題,碰到其餘的平衡二叉樹的題目應該不至於沒有思路。當你領會了個人思路以後, 建議再找幾個題目練手,鞏固一下學習成果。node

110. 平衡二叉樹(簡單)

最簡單的莫過於判斷一個樹是否爲平衡二叉樹了,咱們來看下。算法

題目描述

給定一個二叉樹,判斷它是不是高度平衡的二叉樹。

本題中,一棵高度平衡二叉樹定義爲:

一個二叉樹每一個節點 的左右兩個子樹的高度差的絕對值不超過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。用僞代碼描述就是:數組

if abs(高度(root.left) - 高度(root.right)) <= 1 and root.left 也是平衡二叉樹 and root.right 也是平衡二叉樹:
    print('是平衡二叉樹')
else:
    print('不是平衡二叉樹')

而 root.left 和 root.right 如何判斷是不是二叉平衡樹就和 root 是同樣的了,能夠看出這個問題有明顯的遞歸性。數據結構

所以咱們首先須要知道如何計算一個子樹的高度。這個能夠經過遞歸的方式輕鬆地計算出來。計算子樹高度的 Python 代碼以下:函數

def dfs(node, depth):
    if not node: return 0
    l = dfs(node.left, depth + 1)
    r = dfs(node.right, depth + 1)
    return max(l, r) + 1

代碼

代碼支持: Python3學習

Python3 Code:spa

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        def dfs(node, depth):
            if not node: return 0
            l = dfs(node.left, depth + 1)
            r = dfs(node.right, depth + 1)
            return max(l, r)  + 1
        if not root: return True
        if abs(dfs(root.left, 0) -  dfs(root.right, 0)) > 1: return False
        return self.isBalanced(root.left) and self.isBalanced(root.right)

複雜度分析指針

  • 時間複雜度:對於 isBalanced 來講,因爲每一個節點最多被訪問一次,這部分的時間複雜度爲 $O(N)$,而 dfs 函數 每次被調用的次數不超過 $log N$,所以總的時間複雜度爲 $O(NlogN)$,其中 $N$ 爲 樹的節點總數。
  • 空間複雜度:因爲使用了遞歸,這裏的空間複雜度的瓶頸在棧空間,所以空間複雜度爲 $O(h)$,其中 $h$ 爲樹的高度。

108. 將有序數組轉換爲二叉搜索樹(簡單)

108 和 109 基本是同樣的,只不過數據結構不同,109 變成了鏈表了而已。因爲鏈表操做比數組須要考慮更多的因素,所以 109 是 中等難度。code

題目描述

將一個按照升序排列的有序數組,轉換爲一棵高度平衡二叉搜索樹。

本題中,一個高度平衡二叉樹是指一個二叉樹每一個節點 的左右兩個子樹的高度差的絕對值不超過 1。

示例:

給定有序數組: [-10,-3,0,5,9],

一個可能的答案是:[0,-3,9,-10,null,5],它能夠表示下面這個高度平衡二叉搜索樹:

      0
     / \
   -3   9
   /   /
 -10  5

思路

對於這個問題或者 給定一個二叉搜索樹,將其改成平衡(後面會講) 基本思路都是同樣的。blog

題目的要求是將有序數組轉化爲:

  1. 高度平衡的二叉樹
  2. 二叉搜索樹

因爲平衡二叉樹是左右兩個子樹的高度差的絕對值不超過 1。所以一種簡單的方法是選擇中點做爲根節點,根節點左側的做爲左子樹,右側的做爲右子樹便可。緣由很簡單,這樣分配能夠保證左右子樹的節點數目差不超過 1。所以高度差天然也不會超過 1 了。

上面的操做同時也知足了二叉搜索樹,緣由就是題目給的數組是有序的。

你也能夠選擇別的數做爲根節點,而不是中點,這也能夠看出答案是不惟一的。

代碼

代碼支持: Python3

Python3 Code:

class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
        if not nums: return None
        mid = (len(nums) - 1) // 2
        root = TreeNode(nums[mid])
        root.left = self.sortedArrayToBST(nums[:mid])
        root.right = self.sortedArrayToBST(nums[mid + 1:])
        return root

複雜度分析

  • 時間複雜度:因爲每一個節點最多被訪問一次,所以總的時間複雜度爲 $O(N)$,其中 $N$ 爲數組長度。
  • 空間複雜度:因爲使用了遞歸,這裏的空間複雜度的瓶頸在棧空間,所以空間複雜度爲 $O(h)$,其中 $h$ 爲樹的高度。同時因爲是平衡二叉樹,所以 $h$ 就是 $log N$。

109. 有序鏈表轉換二叉搜索樹(中等)

題目描述

`給定一個單鏈表,其中的元素按升序排序,將其轉換爲高度平衡的二叉搜索樹。

本題中,一個高度平衡二叉樹是指一個二叉樹每一個節點 的左右兩個子樹的高度差的絕對值不超過 1。

示例:

給定的有序鏈表: [-10, -3, 0, 5, 9],

一個可能的答案是:[0, -3, 9, -10, null, 5], 它能夠表示下面這個高度平衡二叉搜索樹:

      0
     / \
   -3   9
   /   /
 -10  5

思路

和 108 思路同樣。 不一樣的是數據結構的不一樣,所以咱們須要關注的是鏈表和數組的操做差別。

(數組的狀況)

咱們再來看下鏈表:


(鏈表的狀況)

找到中點,只須要使用經典的快慢指針便可。同時爲了防止環的出現, 咱們須要斬斷指向 mid 的 next 指針,所以須要記錄一下中點前的一個節點,這隻須要用一個變量 pre 記錄便可。

代碼

代碼支持: Python3

Python3 Code:

class Solution:
    def sortedListToBST(self, head: ListNode) -> TreeNode:
        if not head:
            return head
        pre, slow, fast = None, head, head

        while fast and fast.next:
            fast = fast.next.next
            pre = slow
            slow = slow.next
        if pre:
            pre.next = None
        node = TreeNode(slow.val)
        if slow == fast:
            return node
        node.left = self.sortedListToBST(head)
        node.right = self.sortedListToBST(slow.next)
        return node

複雜度分析

  • 時間複雜度:因爲每一個節點最多被訪問一次,所以總的時間複雜度爲 $O(N)$,其中 $N$ 爲鏈表長度。
  • 空間複雜度:因爲使用了遞歸,這裏的空間複雜度的瓶頸在棧空間,所以空間複雜度爲 $O(h)$,其中 $h$ 爲樹的高度。同時因爲是平衡二叉樹,所以 $h$ 就是 $log N$。

1382. 將二叉搜索樹變平衡(中等)

題目描述

給你一棵二叉搜索樹,請你返回一棵 平衡後 的二叉搜索樹,新生成的樹應該與原來的樹有着相同的節點值。

若是一棵二叉搜索樹中,每一個節點的兩棵子樹高度差不超過 1 ,咱們就稱這棵二叉搜索樹是 平衡的 。

若是有多種構造方法,請你返回任意一種。

 

示例:

輸入:root = [1,null,2,null,3,null,4,null,null]
輸出:[2,1,3,null,null,null,4]
解釋:這不是惟一的正確答案,[3,1,4,null,2,null,null] 也是一個可行的構造方案。
 

提示:

樹節點的數目在 1 到 10^4 之間。
樹節點的值互不相同,且在 1 到 10^5 之間。

思路

因爲二叉搜索樹的中序遍歷是一個有序數組,所以問題很容易就轉化爲 108. 將有序數組轉換爲二叉搜索樹(簡單)

代碼

代碼支持: Python3

Python3 Code:

class Solution:
    def inorder(self, node):
        if not node: return []
        return self.inorder(node.left) + [node.val] + self.inorder(node.right)
    def balanceBST(self, root: TreeNode) -> TreeNode:
        nums = self.inorder(root)
        def dfs(start, end):
            if start == end: return TreeNode(nums[start])
            if start > end: return None
            mid = (start + end) // 2
            root = TreeNode(nums[mid])
            root.left = dfs(start, mid - 1)
            root.right = dfs(mid + 1, end)
            return root
        return dfs(0, len(nums) - 1)

複雜度分析

  • 時間複雜度:因爲每一個節點最多被訪問一次,所以總的時間複雜度爲 $O(N)$,其中 $N$ 爲鏈表長度。
  • 空間複雜度:雖然使用了遞歸,可是瓶頸不在棧空間,而是開闢的長度爲 $N$ 的 nums 數組,所以空間複雜度爲 $O(N)$,其中 $N$ 爲樹的節點總數。

總結

本文經過四道關於二叉平衡樹的題幫助你們識別此類型題目背後的思惟邏輯,咱們來總結一下學到的知識。

平衡二叉樹指的是:一個二叉樹每一個節點的左右兩個子樹的高度差的絕對值不超過1。

若是須要讓你判斷一個樹是不是平衡二叉樹,只須要死扣定義,而後用遞歸便可輕鬆解決。

若是須要你將一個數組或者鏈表(邏輯上都是線性的數據結構)轉化爲平衡二叉樹,只須要隨便選一個節點,並分配一半到左子樹,另外一半到右子樹便可。

同時,若是要求你轉化爲平衡二叉搜索樹,則能夠選擇排序數組(或鏈表)的中點,左邊的元素爲左子樹, 右邊的元素爲右子樹便可。

小提示 1: 若是不須要是二叉搜索樹則不須要排序,不然須要排序。

小提示 2: 你也能夠不選擇中點, 算法須要相應調整,感興趣的同窗能夠試試。

小提示 3: 鏈表的操做須要特別注意環的存在。

相關文章
相關標籤/搜索