目录
一、高度平衡的二叉搜索树简介
参考文献: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/
三、将有序数组转换为二叉搜索树
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/
感悟
总而言之,对于 “树” 类、乃至 “链表” 类问题,在审题、寻找规律、验证想法时,最好在草稿纸上绘制和演示一下简单情况,以试图取得灵感或效果 (例如类似数学归纳法从几个特殊情况推广到一般规律),而不要一昧地心算空想,看着白板发呆。