平衡二叉树专题

力扣关于平衡二叉树的题目仍是有一些的,而且都很是经典,推荐你们练习。今天给你们精选了 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: 链表的操做须要特别注意环的存在。

相关文章
相关标签/搜索