如何从有序数组构建平衡BST并确保重复元素仅存在于右子树
如何从有序数组构建平衡BST并确保重复元素仅存在于右子树
我完全get到你的问题了!你现在的代码能把有序数组转成平衡BST,但碰到重复元素时,会有等于根节点的元素跑到左子树里(比如示例中层序遍历里的那个第三层的4),违反了你设定的规则——左子树所有节点必须小于当前节点,右子树必须大于等于当前节点。咱们来一步步把这个问题解决掉~
现有代码的问题分析
你的核心逻辑是每次取区间中间点作为根节点,递归构建左右子树。但当数组存在重复元素时,中间点左侧可能存在和根节点值相等的元素,这些元素会被递归到左子树,直接违反你的BST规则。比如示例数组里索引3的4,和根节点(索引4的4)值相等,却被放到了左子树的分支里,这就是问题的根源。
解决方案思路
要同时满足两个核心要求:
- BST规则:左子树全小于当前节点,右子树全大于等于当前节点;
- 平衡性:左右子树高度差不超过1,保证树是平衡的。
关键调整点:每次选择根节点时,取当前区间内第一个等于中间值的元素作为根节点,这样左区间只会包含小于该值的元素,右区间包含大于等于的元素。同时,因为我们从原区间中间点出发找重复元素,根节点位置依然靠近区间中心,不会破坏树的平衡性。
修改后的完整代码
#include <iostream> struct node { int data; node* left; node* right; node(int d, node* l = nullptr, node* r = nullptr) : data(d), left(l), right(r) {} }; void release(node* root) { if (!root) return; release(root->left); release(root->right); delete root; } // 二分查找:在区间[l, r]内找到第一个等于val的元素索引 int findFirst(const int* arr, int l, int r, int val) { int res = r; while (l <= r) { int mid = (l + r) / 2; if (arr[mid] == val) { res = mid; r = mid - 1; // 继续向左寻找更早的重复元素 } else if (arr[mid] < val) { l = mid + 1; } else { r = mid - 1; } } return res; } node* toTree(const int* sorted_data, int l, int r) { if (l > r) return nullptr; size_t m = (l + r) / 2; int val = sorted_data[m]; // 找到第一个等于val的位置作为根节点,确保左区间全小于val int m_prime = findFirst(sorted_data, l, r, val); node* root = new node(sorted_data[m_prime]); // 左子树仅处理小于val的区间 root->left = toTree(sorted_data, l, m_prime - 1); // 右子树处理大于等于val的区间 root->right = toTree(sorted_data, m_prime + 1, r); return root; } void printInorder(node* root) { if (!root) return; printInorder(root->left); std::cout << root->data << ' '; printInorder(root->right); } // 新增层序遍历,方便验证树结构 void printLevelOrder(node* root) { if (!root) return; node* queue[100]; int front = 0, rear = 0; queue[rear++] = root; while (front < rear) { node* curr = queue[front++]; std::cout << curr->data << ' '; if (curr->left) queue[rear++] = curr->left; if (curr->right) queue[rear++] = curr->right; } } int main() { int arr[9] = { 1, 3, 3, 4, 4, 5, 6, 6, 7 }; node* tree = toTree(arr, 0, 8); std::cout << "中序遍历结果:"; printInorder(tree); std::cout << "\n层序遍历结果:"; printLevelOrder(tree); release(tree); }
关键代码解释
- findFirst函数:用二分法高效定位第一个等于目标值的元素,比线性扫描更适合大数组,时间复杂度为O(logn)。
- 修改后的toTree函数:
- 先取区间中间值作为参考,保证根节点位置靠近区间中心;
- 找到第一个等于该值的位置作为根节点,确保左区间所有元素都小于当前根节点值;
- 右区间自然包含所有大于等于根节点值的元素,完全符合你的BST规则;
- 根节点位置靠近区间中心,左右子树的区间长度差不会超过1,保证了树的平衡性。
结果验证
运行修改后的代码,你会发现:
- 中序遍历结果和原数组完全一致,说明BST的排序逻辑正确;
- 所有等于根节点的元素都出现在右子树分支中,完全符合你设定的规则;
- 层序遍历结果显示树的结构是平衡的,左右子树高度差不超过1。
内容来源于stack exchange
相关产品推荐
相关产品推荐

