【二叉搜索树】(四) 高度平衡的二叉搜索树

目录

一、高度平衡的二叉搜索树简介

二、平衡二叉树

2.1 题目要求

2.2 解决过程

三、将有序数组转换为二叉搜索树

3.1 题目要求

3.2 解决过程


一、高度平衡的二叉搜索树简介

 参考文献:https://leetcode-cn.com/leetbook/read/introduction-to-data-structure-binary-search-tree/xmhcp1/


二、平衡二叉树

2.1 题目要求

2.2 解决过程

个人实现

法一:递归。通过遍历二叉树来判断是否需要修改标记 flag,否则保持默认状态 True。空间复杂度 O(n),时间复杂度 O(n)。

2020/08/15 - 84.33% (60ms)

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        def recur(root):
            if not root:
                return -1
            
            left = recur(root.left) + 1  # 左子树高度
            right = recur(root.right) + 1  # 右子树高度
            if abs(left-right) > 1:  # 不满足条件, 标记为否
                nonlocal flag
                flag = False
            return max(left, right)  # 返回当前节点高度
        
        flag = True  # 是否为高度平衡二叉树标记
        recur(root)  # 递归检查
        return flag

法一:递归。通过递归,从叶子节点开始获取各节点高度。若高度差绝对值大于 1,则不满足高度平衡二叉树的要求,返回 -1 (替代 False)。高度获取到 -1 的节点将持续保留该状态,直至递归结束。否则,返回正常高度。最后通过判断语句 (根节点高度是否为 -1) 确认是否为高度平衡二叉树。空间复杂度 O(n),时间复杂度 O(n)。

2020/08/15 - 92.56% (56ms)

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        def recur(root):
            if not root:
                return 0
            
            left = recur(root.left)  # 左子树高度
            right = recur(root.right)  # 右子树高度
            
            # -1 作为子树高度差的绝对值不超过 1 的标志, 不断保留到最后
            if left == -1 or right == -1 or abs(left-right) > 1:  # 关键
                return -1
            else:
                return max(left, right) + 1  # 高度差绝对值满足, 返回父节点高度

        return recur(root) != -1  # 因为难以既返回 int 又返回 bool, 所以通过 int 来判断

其他实现与说明

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        return self.recur(root) != -1

    def recur(self, root):
        if not root: 
            return 0
        left = self.recur(root.left)
        if left == -1: 
            return -1
        right = self.recur(root.right)
        if right == -1: 
            return -1
        return max(left, right) + 1 if abs(left - right) < 2 else -1

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        if not root: 
            return True
        return abs(self.depth(root.left) - self.depth(root.right)) <= 1 and \
            self.isBalanced(root.left) and self.isBalanced(root.right)
    
    # 每次都需要计算深度, 非常低效
    def depth(self, root):
        if not root: 
            return 0
        return max(self.depth(root.left), self.depth(root.right)) + 1

参考文献

https://leetcode-cn.com/leetbook/read/introduction-to-data-structure-binary-search-tree/xmx4r7/

https://leetcode-cn.com/problems/balanced-binary-tree/solution/balanced-binary-tree-di-gui-fang-fa-by-jin40789108/


三、将有序数组转换为二叉搜索树

3.1 题目要求

3.2 解决过程

个人实现

法一:自底向上递归 + 二分法思想

首先,处理空树这一特殊情况。

其次,构造递归函数 recur() 以生成高度平衡 BST。形参部分,root 表示 当前节点left right 是数组 nums 的 限定索引范围。当 left == right 时,限定索引范围异常,相同索引指定唯一数组元素,表示到达 叶子节点 (外部节点) 位置了,故返回以 nums[left] 为节点值的节点,以其供 父节点 (内部节点) 设置孩子节点。否则 left < right,限定索引范围正常,在索引范围内根据公式计算中间值 mid,以 nums[mid] 为节点值生成当前节点 root。

再次,根据二分法思想,在确保范围收缩后数值正常 (这很重要!!!) 的情况下,分别递归设置当前节点的 左子节点 root.left右子节点 root.right

最后,返回当前节点 root,以其供 父节点 (内部节点) 设置孩子节点。

2020/08/15 - 93.66% (52ms) - 较好

class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
        if not nums:
            return None
        
        def recur(root, left, right):
            if left == right:
                return TreeNode(nums[left])  # 返回叶子节点
            
            # 设置当前接节点
            mid = left + (right-left) // 2  # 中间值 mid = (left + right) // 2
            root = TreeNode(nums[mid])  # 取中间值作为当前节点值
            
            # 只有在确保数值范围正常时, 才设置左、右子节点, 否则默认为空子
            if mid-1 >= left:
                root.left = recur(root.left, left, mid-1)  # 设置左子节点
            if mid+1 <= right:
                root.right = recur(root.right, mid+1, right)  # 设置右子节点
                
            return root  # 返回当前节点
            
        return recur(None, 0, len(nums)-1)  # 给定最初的数值范围

官方实现与说明


## 对个人实现法一的进一步简化去冗余
class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
        def helper(left, right):
            if left > right:
                return None

            # 总是选择中间位置左边的数字作为根节点
            mid = (left + right) // 2

            root = TreeNode(nums[mid])
            root.left = helper(left, mid - 1)
            root.right = helper(mid + 1, right)
            return root

        return helper(0, len(nums) - 1)

2020/08/15 - 82.45% (56ms)


class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
        def helper(left, right):
            if left > right:
                return None

            # 总是选择中间位置右边的数字作为根节点
            mid = (left + right + 1) // 2

            root = TreeNode(nums[mid])
            root.left = helper(left, mid - 1)
            root.right = helper(mid + 1, right)
            return root

        return helper(0, len(nums) - 1)

2020/08/15 - 82.45% (56ms)


class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
        def helper(left, right):
            if left > right:
                return None

            # 选择任意一个中间位置数字作为根节点
            mid = (left + right + randint(0, 1)) // 2

            root = TreeNode(nums[mid])
            root.left = helper(left, mid - 1)
            root.right = helper(mid + 1, right)
            return root

        return helper(0, len(nums) - 1)

2020/08/15 - 63.69% (60ms) 


其他实现与说明

## 严格来说这种用切片的方法没有上述用索引范围的方法高效, 因为切片操作的复杂度为 O(k)
class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
        if not nums:
            return None
        
        # 找到中点作为根节点
        mid = len(nums) // 2
        node = TreeNode(nums[mid])

        # 左侧数组作为左子树
        left = nums[:mid]
        right = nums[mid+1:]

        # 递归调用
        node.left = self.sortedArrayToBST(left)
        node.right = self.sortedArrayToBST(right)

        return node

参考文献

https://leetcode-cn.com/leetbook/read/introduction-to-data-structure-binary-search-tree/xm5go5/

https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/solution/jiang-you-xu-shu-zu-zhuan-huan-wei-er-cha-sou-s-33/

https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/solution/tu-jie-er-cha-sou-suo-shu-gou-zao-di-gui-python-go/


感悟

总而言之,对于 “树” 类、乃至 “链表” 类问题,在审题、寻找规律、验证想法时,最好在草稿纸上绘制和演示一下简单情况,以试图取得灵感或效果 (例如类似数学归纳法从几个特殊情况推广到一般规律),而不要一昧地心算空想,看着白板发呆。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值