leetcode地址:二叉搜索子树的最大键值和
给你一棵以 root 为根的 二叉树 ,请你返回 任意 二叉搜索子树的最大键值和。
二叉搜索树的定义如下:
任意节点的左子树中的键值都 小于 此节点的键值。
任意节点的右子树中的键值都 大于 此节点的键值。
任意节点的左子树和右子树都是二叉搜索树。
示例 1:
输入:root = [1,4,3,2,4,2,5,null,null,null,null,null,null,4,6]
输出:20
解释:键值为 3 的子树是和最大的二叉搜索树。
示例 2:
输入:root = [4,3,null,1,2]
输出:2
解释:键值为 2 的单节点子树是和最大的二叉搜索树。
示例 3:
输入:root = [-4,-2,-5]
输出:0
解释:所有节点键值都为负数,和最大的二叉搜索树为空。
示例 4:
输入:root = [2,1,3]
输出:6
示例 5:
输入:root = [5,4,8,3,null,6,3]
输出:7
提示:
每棵树有 1 到 40000 个节点。
每个节点的键值在 [-4 * 10^4 , 4 * 10^4] 之间。
实现思路
要解决这个问题,我们需要计算二叉树中每一个子树是否是二叉搜索树(BST),并计算所有BST子树的键值和,最终返回这些BST子树中的最大键值和。
我们可以使用递归的方法来解决这个问题。递归地遍历树中的每一个节点,同时计算子树的信息,包括:
- 是否是BST
- 子树的键值和
- 子树的最小键值
- 子树的最大键值
具体步骤如下:
- 定义一个辅助函数 dfs(node),它返回四个值:
is_bst:当前子树是否为BST
subtree_sum:当前子树的键值和
min_val:当前子树的最小键值
max_val:当前子树的最大键值 递归计算左子树和右子树的信息。 - 根据左子树和右子树的信息,以及当前节点的值,判断当前子树是否为BST。
- 如果是BST,则计算当前子树的键值和、最小键值和最大键值。
- 更新最大BST键值和的结果。
- 返回当前子树的信息。
代码详解
假设有以下二叉树:
1
/ \
4 3
/ / \
2 2 5
首先定义树节点类 TreeNode,用于构建二叉树:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
TreeNode 类有三个属性:
val:节点的值。
left:左子树。
right:右子树。
接下来是主函数 max_sum_BST,它计算二叉树中任意二叉搜索子树的最大键值和:
def max_sum_BST(root):
def dfs(node):
if not node:
# 空节点被认为是BST,其和为0,最小值为正无穷大,最大值为负无穷大
return True, 0, float('inf'), float('-inf')
dfs 辅助函数
dfs 函数接收一个节点 node 作为参数,返回四个值:
is_bst:布尔值,表示当前子树是否为BST。
subtree_sum:当前子树的键值和。
min_val:当前子树的最小键值。
max_val:当前子树的最大键值。
若 node 为 None,即为空节点,则返回:
True:空节点被认为是BST。
0:空节点的键值和为0。
float('inf'):空节点的最小键值为正无穷大。
float('-inf'):空节点的最大键值为负无穷大。
left_is_bst, left_sum, left_min, left_max = dfs(node.left)
right_is_bst, right_sum, right_min, right_max = dfs(node.right)
递归计算左子树和右子树的信息,包括是否为BST、键值和、最小键值和最大键值。
# 检查当前节点为根的子树是否为BST
if left_is_bst and right_is_bst and left_max < node.val < right_min:
subtree_sum = left_sum + node.val + right_sum
max_sum[0] = max(max_sum[0], subtree_sum)
return True, subtree_sum, min(left_min, node.val), max(right_max, node.val)
else:
# 当前子树不是BST,返回假
return False, 0, float('-inf'), float('inf')
检查当前节点为根的子树是否为BST,条件是:
左子树是BST。
右子树是BST。
当前节点的值大于左子树的最大值且小于右子树的最小值。
若满足上述条件,则当前子树是BST:
计算当前子树的键值和 subtree_sum。
更新全局最大BST键值和 max_sum[0]。
返回 True、subtree_sum、当前子树的最小键值和最大键值。
否则,当前子树不是BST,返回 False、键值和0、最小键值为负无穷大、最大键值为正无穷大。
max_sum = [0]
dfs(root)
return max_sum[0]
在 max_sum_BST 函数中:
max_sum 是一个列表,存储最大BST键值和。使用列表是为了在 dfs 函数中能够修改其值。
调用 dfs(root),开始递归遍历树。
返回 max_sum[0],即最大BST键值和。
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def max_sum_BST(root):
def dfs(node):
if not node:
# 空节点被认为是BST,其和为0,最小值为正无穷大,最大值为负无穷大
return True, 0, float('inf'), float('-inf')
left_is_bst, left_sum, left_min, left_max = dfs(node.left)
right_is_bst, right_sum, right_min, right_max = dfs(node.right)
# 检查当前节点为根的子树是否为BST
if left_is_bst and right_is_bst and left_max < node.val < right_min:
subtree_sum = left_sum + node.val + right_sum
max_sum[0] = max(max_sum[0], subtree_sum)
return True, subtree_sum, min(left_min, node.val), max(right_max, node.val)
else:
# 当前子树不是BST,返回假
return False, 0, float('-inf'), float('inf')
max_sum = [0]
dfs(root)
return max_sum[0]
# 测试示例
if __name__ == "__main__":
# 创建测试二叉树
# 1
# / \
# 4 3
# / / \
# 2 2 5
root = TreeNode(1)
root.left = TreeNode(4)
root.right = TreeNode(3)
root.left.left = TreeNode(2)
root.right.left = TreeNode(2)
root.right.right = TreeNode(5)
result = max_sum_BST(root)
print(f"最大BST子树的键值和: {result}") # 应该输出6
GO语言实现
package main
import (
"fmt"
"math"
)
// TreeNode 定义二叉树节点
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
// maxSumBST 函数返回二叉树中任意二叉搜索子树的最大键值和
func maxSumBST(root *TreeNode) int {
maxSum := 0
// dfs 辅助函数返回四个值:是否是BST、子树键值和、最小键值、最大键值
var dfs func(node *TreeNode) (bool, int, int, int)
dfs = func(node *TreeNode) (bool, int, int, int) {
if node == nil {
// 空节点被认为是BST,其和为0,最小值为正无穷大,最大值为负无穷大
return true, 0, math.MaxInt64, math.MinInt64
}
leftIsBST, leftSum, leftMin, leftMax := dfs(node.Left)
rightIsBST, rightSum, rightMin, rightMax := dfs(node.Right)
// 检查当前节点为根的子树是否为BST
if leftIsBST && rightIsBST && leftMax < node.Val && node.Val < rightMin {
subtreeSum := leftSum + node.Val + rightSum
if subtreeSum > maxSum {
maxSum = subtreeSum
}
return true, subtreeSum, min(leftMin, node.Val), max(rightMax, node.Val)
}
// 当前子树不是BST,返回假
return false, 0, math.MinInt64, math.MaxInt64
}
dfs(root)
return maxSum
}
// 辅助函数:返回两个整数中的较小值
func min(a, b int) int {
if a < b {
return a
}
return b
}
// 辅助函数:返回两个整数中的较大值
func max(a, b int) int {
if a > b {
return a
}
return b
}
// 测试示例
func main() {
// 创建测试二叉树
// 1
// / \
// 4 3
// / / \
// 2 2 5
root := &TreeNode{Val: 1}
root.Left = &TreeNode{Val: 4}
root.Right = &TreeNode{Val: 3}
root.Left.Left = &TreeNode{Val: 2}
root.Right.Left = &TreeNode{Val: 2}
root.Right.Right = &TreeNode{Val: 5}
result := maxSumBST(root)
fmt.Printf("最大BST子树的键值和: %d\n", result) // 应该输出6
}
kotlin实现
// 定义二叉树节点类
class TreeNode(var `val`: Int) {
var left: TreeNode? = null
var right: TreeNode? = null
}
// maxSumBST 函数返回二叉树中任意二叉搜索子树的最大键值和
fun maxSumBST(root: TreeNode?): Int {
var maxSum = 0
// dfs 辅助函数返回四个值:是否是BST、子树键值和、最小键值、最大键值
fun dfs(node: TreeNode?): Quad<Boolean, Int, Int, Int> {
if (node == null) {
// 空节点被认为是BST,其和为0,最小值为正无穷大,最大值为负无穷大
return Quad(true, 0, Int.MAX_VALUE, Int.MIN_VALUE)
}
val (leftIsBST, leftSum, leftMin, leftMax) = dfs(node.left)
val (rightIsBST, rightSum, rightMin, rightMax) = dfs(node.right)
// 检查当前节点为根的子树是否为BST
if (leftIsBST && rightIsBST && leftMax < node.`val` && node.`val` < rightMin) {
val subtreeSum = leftSum + node.`val` + rightSum
if (subtreeSum > maxSum) {
maxSum = subtreeSum
}
return Quad(true, subtreeSum, minOf(leftMin, node.`val`), maxOf(rightMax, node.`val`))
}
// 当前子树不是BST,返回假
return Quad(false, 0, Int.MIN_VALUE, Int.MAX_VALUE)
}
dfs(root)
return maxSum
}
// Quad 数据类,用于返回四个值
data class Quad<A, B, C, D>(val first: A, val second: B, val third: C, val fourth: D)
// 测试示例
fun main() {
// 创建测试二叉树
// 1
// / \
// 4 3
// / / \
// 2 2 5
val root = TreeNode(1)
root.left = TreeNode(4)
root.right = TreeNode(3)
root.left?.left = TreeNode(2)
root.right?.left = TreeNode(2)
root.right?.right = TreeNode(5)
val result = maxSumBST(root)
println("最大BST子树的键值和: $result") // 应该输出6
}