DP24 最优BST Optimal Binary Search Tree @geeksforgeeks

本文详细介绍了如何使用动态规划解决最优二叉搜索树问题,包括最优子结构、重叠子问题等概念,并提供了递归和动态规划两种实现方式的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Given a sorted array keys[0.. n-1] of search keys and an array freq[0.. n-1] of frequency counts, wherefreq[i] is the number of searches to keys[i]. Construct a binary search tree of all keys such that the total cost of all the searches is as small as possible.

Let us first define the cost of a BST. The cost of a BST node is level of that node multiplied by its frequency. Level of root is 1.

Example 1
Input:  keys[] = {10, 12}, freq[] = {34, 50}
There can be following two possible BSTs 
        10                       12
          \                     / 
           12                 10
          I                     II
Frequency of searches of 10 and 12 are 34 and 50 respectively.
The cost of tree I is 34*1 + 50*2 = 134
The cost of tree II is 50*1 + 34*2 = 118 

Example 2
Input:  keys[] = {10, 12, 20}, freq[] = {34, 8, 50}
There can be following possible BSTs
    10                12                 20         10              20
      \             /    \              /             \            /
      12          10     20           12               20         10  
        \                            /                 /           \
         20                        10                12             12  
     I               II             III             IV             V
Among all possible BSTs, cost of the fifth BST is minimum.  
Cost of the fifth BST is 1*50 + 2*34 + 3*8 = 142

1) Optimal Substructure:
The optimal cost for freq[i..j] can be recursively calculated using following formula.
optCost(i, j) = \sum\limits_{k=i}^j freq[k] + \min\limits_{r=i}^j  [ optCost(i, r-1) + optCost(r+1, j) ]

We need to calculate optCost(0, n-1) to find the result.

The idea of above formula is simple, we one by one try all nodes as root (r varies from i to j in second term). When we make rth node as root, we recursively calculate optimal cost from i to r-1 and r+1 to j.
We add sum of frequencies from i to j (see first term in the above formula), this is added because every search will go through root and one comparison will be done for every search.

2) Overlapping Subproblems
Following is recursive implementation that simply follows the recursive structure mentioned above.


Dynamic Programming Solution
Following is C/C++ implementation for optimal BST problem using Dynamic Programming. We use an auxiliary array cost[n][n] to store the solutions of subproblems. cost[0][n-1] will hold the final result. The challenge in implementation is, all diagonal values must be filled first, then the values which lie on the line just above the diagonal. In other words, we must first fill all cost[i][i] values, then all cost[i][i+1] values, then all cost[i][i+2] values. So how to fill the 2D array in such manner> The idea used in the implementation is same as Matrix Chain Multiplication problem, we use a variable ‘L’ for chain length and increment ‘L’, one by one. We calculate column number ‘j’ using the values of ‘i’ and ‘L’.


package DP;

public class OptimalBinarySearchTree {

	public static void main(String[] args) {
		int[] keys = {10,12,20};
		int[] freq = {34,8,50};
		int n = keys.length;
		System.out.println("Cost of Optimal BST is " + optimalSearchTreeRec(keys, freq, n));
		System.out.println("Cost of Optimal BST is " + optimalSearchTreeDP(keys, freq, n));
	}
	
	public static int optCost(int[] freq, int begin, int end){
		if(end < begin){		// If there are no elements in this subarray
			return 0;
		}
		if(begin == end){		// If there is one element in this subarray
			return freq[begin];
		}
		
		// 每往下走一步,都要加上从begin到end的freq和
		int fsum = sum(freq, begin, end);	// Get sum of freq[i], freq[i+1], ... freq[j]
		int min = Integer.MAX_VALUE;		// Initialize minimum value
		
		// One by one consider all elements as root and recursively find cost
		// of the BST, compare the cost with min and update min if needed
		for(int r=begin; r<=end; r++){
			int cost = optCost(freq, begin, r-1) + optCost(freq, r+1, end);
			min = Math.min(min, cost);
		}
		
		return min + fsum;		// Return minimum value
	}
	
	// A utility function to get sum of array elements freq[i] to freq[j]
	public static int sum(int[] freq, int begin, int end){
		int sum = 0;
		for(int i=begin; i<=end; i++){
			sum += freq[i];
		}
		return sum;
	}
	
	// The main function that calculates minimum cost of a Binary Search Tree.
	// It mainly uses optCost() to find the optimal cost.
	public static int optimalSearchTreeRec(int[] keys, int[] freq, int n){
		// Here array keys[] is assumed to be sorted in increasing order.
	     // If keys[] is not sorted, then add code to sort keys, and rearrange
	     // freq[] accordingly.
		return optCost(freq, 0, n-1);
	}

	/* A Dynamic Programming based function that calculates minimum cost of
	   a Binary Search Tree. */
	public static int optimalSearchTreeDP(int[] keys, int[] freq, int n){
		int[][] cost = new int[n][n];		 /* Create an auxiliary 2D matrix to store results of subproblems */
		
		/* cost[i][j] = Optimal cost of binary search tree that can be
	       formed from keys[i] to keys[j].
	       cost[0][n-1] will store the resultant cost */
		for(int i=0; i<n; i++){
			cost[i][i] = freq[i];	// 只有一个元素时,cost为freq,就是把那个元素作为root
		}
		
		// Now we need to consider chains of length 2, 3, ... .
		for(int L=2; L<=n; L++){		// L is chain length.
			for(int begin=0; begin<n-L+1; begin++){		// begin: begin index in keys
				int end = begin+L-1;		// end: end index in keys
				cost[begin][end] = Integer.MAX_VALUE;
				
				// Try making all keys in interval keys[begin..end] as root
				for(int r=begin; r<=end; r++){
					// c = cost when keys[r] becomes root of this subtree
					int c = (r>begin ? cost[begin][r-1] : 0) + (r<end ? cost[r+1][end] : 0) + sum(freq, begin, end);
					cost[begin][end] = Math.min(cost[begin][end], c);
				}
			}
		}
		
		return cost[0][n-1];
	}
	
	
}



http://www.geeksforgeeks.org/dynamic-programming-set-24-optimal-binary-search-tree/

<think>嗯,用户想了解关于完整二叉搜索树的概念和实现方法。首先,我需要明确什么是完整二叉搜索树(Complete Binary Search Tree,CBST)。根据之前学过的知识,二叉树中的完全二叉树(Complete Binary Tree)是指除了最后一层外,其他层都是满的,并且最后一层的节点都尽可能靠左排列。而二叉搜索树(Binary Search TreeBST)的特点是每个节点的左子树所有节点的值都小于该节点的值,右子树则都大于。所以完整二叉搜索树应该是结合了这两种特性的数据结构,即同时满足完全二叉树的结构和二叉搜索树的排序性质。 接下来,我需要确认完整二叉搜索树的具体定义和特点。完全二叉树的特性使得它可以用数组高效地存储,而不需要指针,这对于内存管理是有优势的。而二叉搜索树的特性则保证了高效的查找、插入和删除操作,平均时间复杂度为O(log n)。结合起来,CBST可能在保持高效操作的同时,优化存储结构。不过,用户可能想知道如何实现这样的数据结构,或者它的具体应用场景。 然后,实现方法方面,可能需要考虑如何构建这样的树。由于是完全二叉树,节点的位置可以通过数组的索引来确定,比如对于节点i,左子节点在2i+1,右子节点在2i+2。同时,作为二叉搜索树,必须满足左子树的值小于根节点,右子树的值大于根节点。因此,构建CBST可能需要将给定的有序数组按照完全二叉树的结构进行排列。例如,对于有序数组,中序遍历完全二叉树的结构应该能得到有序序列,因此可能需要找到中间节点作为根,递归构建左子树和右子树。 另外,用户可能关心插入和删除操作在CBST中的处理。因为完全二叉树的结构要求最后一层尽可能左对齐,插入和删除可能需要调整树的结构以维持完全性,这可能会影响二叉搜索树的性质。这时候可能需要特定的算法来保持两者的平衡,或者在某些情况下,CBST可能更适合静态数据,而不是频繁变动的数据,因为维护结构可能代价较高。 还需要考虑CBST与其他类型二叉搜索树的区别,比如平衡二叉搜索树(如AVL树、红黑树)。CBST可能在存储上更紧凑,但可能在动态操作上不如平衡树高效。因此,用户可能想知道在什么情况下选择CBST更合适,比如数据静态且需要快速查找的情况下。 此外,用户的问题中提到了“实现方法”,可能需要给出具体的代码示例,比如如何从有序数组构建CBST,或者如何插入节点。例如,可以通过递归方法,每次选择中间元素作为根节点,然后递归构建左右子树,确保完全二叉树的结构。这部分需要结合算法步骤和代码实现来说明。 最后,需要验证自己的理解是否正确,是否有遗漏或错误的地方。例如,是否混淆了完全二叉树和满二叉树的概念,或者是否正确处理了二叉搜索树的排序性质与完全二叉树结构之间的冲突。比如,在构建过程中,如何确保节点位置既满足完全性,又满足左小右大的顺序,可能需要特定的构建策略,如中序遍历填充。</think>### 完整二叉搜索树(Complete Binary Search Tree)概念解析 完整二叉搜索树是**完全二叉树**和**二叉搜索树**的结合体,需同时满足以下特性: 1. **完全二叉树结构**:所有层除最后一层外均为满层,最后一层节点左对齐排列[^1] 2. **二叉搜索树性质**:任意节点的左子树值均小于该节点,右子树值均大于该节点 $$ \text{设节点索引为}i,\ \text{左子节点索引}=2i+1,\ \text{右子节点索引}=2i+2 $$ ### 核心实现步骤(以数组存储为例) 1. **数据预处理**:将输入数据排序为升序序列 2. **递归构建**: ```python def build_cbst(sorted_arr, tree, root_index=0): if not sorted_arr: return n = len(sorted_arr) mid = n // 2 # 计算中间索引 tree[root_index] = sorted_arr[mid] # 递归构建左子树(左半部分) build_cbst(sorted_arr[:mid], tree, 2*root_index+1) # 递归构建右子树(右半部分) build_cbst(sorted_arr[mid+1:], tree, 2*root_index+2) ``` 3. **空间分配**:根据完全二叉树特性预分配数组空间 $$ \text{数组长度}=2^h-1\ \text{(h为树的高度)} $$ ### 关键特性对比 | 特性 | 普通BST | 完整BST | |---------------------|---------|--------------| | 空间利用率 | 动态 | 100%(数组) | | 查找时间复杂度 | O(h) | O(log n) | | 插入/删除复杂度 | O(h) | O(n) | | 适合场景 | 动态数据| 静态数据 | ### 应用场景 1. 预先生成的静态数据集快速查询(如字典数据) 2. 需要固定存储空间的嵌入式系统 3. 需要快速序列化/反序列化的场景[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值