区间动态规划例题:最优二叉搜索树问题
字数 1472 2025-10-27 19:14:05

区间动态规划例题:最优二叉搜索树问题

题目描述
给定一个有序键值集合 keys[1..n] 和对应的搜索频率 freq[1..n],其中 keys[i] 的搜索频率为 freq[i]。要求构建一棵二叉搜索树(BST),使得所有键值的总搜索成本最小。搜索成本定义为键值的深度(从根节点开始为1)乘以频率的总和。

形式化定义:对于键值 keys[i],其深度为 depth(i),总成本为 sum(freq[i] * depth(i)) for i=1 to n。需要构造BST使得该总成本最小。

解题思路
这个问题可以使用区间动态规划解决。定义 dp[i][j] 表示由键值 keys[i..j] 构建的最优二叉搜索树的最小总成本。我们需要考虑每个可能的根节点,计算左右子树的成本加上当前子树所有节点的频率和(因为根节点的选择会影响所有节点的深度)。

详细步骤

  1. 状态定义
    定义 dp[i][j] 为构建 keys[i..j] 的最优BST的最小总成本。
    定义 prefixSum 数组,prefixSum[i] 表示 freq[1] 到 freq[i] 的和,用于快速计算区间频率和。

  2. 状态转移方程
    对于区间 [i, j],依次尝试每个键 k (i ≤ k ≤ j) 作为根节点:

    • 左子树 [i, k-1] 的成本:dp[i][k-1](如果 i > k-1 则为0)
    • 右子树 [k+1, j] 的成本:dp[k+1][j](如果 k+1 > j 则为0)
    • 当前根节点 k 的深度贡献:由于作为根节点,所有节点深度+1,所以需要加上整个区间 [i, j] 的频率和
      因此:dp[i][j] = min(dp[i][k-1] + dp[k+1][j] + sum(freq[i..j])) for k in [i, j]
  3. 初始化

    • 单个节点:dp[i][i] = freq[i](深度为1)
    • 空子树:当 i > j 时,dp[i][j] = 0
  4. 计算顺序
    按区间长度从小到大计算:

    • 先计算长度为1的区间(单个键)
    • 然后计算长度为2、3...直到n
  5. 最终结果
    dp[1][n] 即为由所有键构建的最优BST的最小总成本

举例说明
keys = [10, 12, 20],freq = [34, 8, 50]

计算 prefixSum:[34, 42, 92]

长度1:

  • dp[1][1] = 34
  • dp[2][2] = 8
  • dp[3][3] = 50

长度2:

  • [1,2]:根10:dp[2][2]+sum=8+42=50;根12:dp[1][1]+sum=34+42=76 → min=50
  • [2,3]:根12:dp[3][3]+sum=50+58=108;根20:dp[2][2]+sum=8+58=66 → min=66

长度3 [1,3]:

  • 根10:dp[2][3]+sum=66+92=158
  • 根12:dp[1][1]+dp[3][3]+sum=34+50+92=176
  • 根20:dp[1][2]+sum=50+92=142
    最小成本为142,对应根为20的BST。

算法复杂度

  • 时间复杂度:O(n³),需要遍历所有区间和根节点
  • 空间复杂度:O(n²),用于存储dp表

这个算法通过动态规划系统地考虑了所有可能的BST结构,确保了找到全局最优解。

区间动态规划例题:最优二叉搜索树问题 题目描述 给定一个有序键值集合 keys[ 1..n] 和对应的搜索频率 freq[ 1..n],其中 keys[ i] 的搜索频率为 freq[ i ]。要求构建一棵二叉搜索树(BST),使得所有键值的总搜索成本最小。搜索成本定义为键值的深度(从根节点开始为1)乘以频率的总和。 形式化定义:对于键值 keys[ i],其深度为 depth(i),总成本为 sum(freq[ i] * depth(i)) for i=1 to n。需要构造BST使得该总成本最小。 解题思路 这个问题可以使用区间动态规划解决。定义 dp[ i][ j] 表示由键值 keys[ i..j ] 构建的最优二叉搜索树的最小总成本。我们需要考虑每个可能的根节点,计算左右子树的成本加上当前子树所有节点的频率和(因为根节点的选择会影响所有节点的深度)。 详细步骤 状态定义 定义 dp[ i][ j] 为构建 keys[ i..j ] 的最优BST的最小总成本。 定义 prefixSum 数组,prefixSum[ i] 表示 freq[ 1] 到 freq[ i ] 的和,用于快速计算区间频率和。 状态转移方程 对于区间 [ i, j ],依次尝试每个键 k (i ≤ k ≤ j) 作为根节点: 左子树 [ i, k-1] 的成本:dp[ i][ k-1 ](如果 i > k-1 则为0) 右子树 [ k+1, j] 的成本:dp[ k+1][ j ](如果 k+1 > j 则为0) 当前根节点 k 的深度贡献:由于作为根节点,所有节点深度+1,所以需要加上整个区间 [ i, j ] 的频率和 因此:dp[ i][ j] = min(dp[ i][ k-1] + dp[ k+1][ j] + sum(freq[ i..j])) for k in [ i, j ] 初始化 单个节点:dp[ i][ i] = freq[ i ](深度为1) 空子树:当 i > j 时,dp[ i][ j ] = 0 计算顺序 按区间长度从小到大计算: 先计算长度为1的区间(单个键) 然后计算长度为2、3...直到n 最终结果 dp[ 1][ n ] 即为由所有键构建的最优BST的最小总成本 举例说明 keys = [ 10, 12, 20],freq = [ 34, 8, 50 ] 计算 prefixSum:[ 34, 42, 92 ] 长度1: dp[ 1][ 1 ] = 34 dp[ 2][ 2 ] = 8 dp[ 3][ 3 ] = 50 长度2: [ 1,2]:根10:dp[ 2][ 2]+sum=8+42=50;根12:dp[ 1][ 1 ]+sum=34+42=76 → min=50 [ 2,3]:根12:dp[ 3][ 3]+sum=50+58=108;根20:dp[ 2][ 2 ]+sum=8+58=66 → min=66 长度3 [ 1,3 ]: 根10:dp[ 2][ 3 ]+sum=66+92=158 根12:dp[ 1][ 1]+dp[ 3][ 3 ]+sum=34+50+92=176 根20:dp[ 1][ 2 ]+sum=50+92=142 最小成本为142,对应根为20的BST。 算法复杂度 时间复杂度:O(n³),需要遍历所有区间和根节点 空间复杂度:O(n²),用于存储dp表 这个算法通过动态规划系统地考虑了所有可能的BST结构,确保了找到全局最优解。