You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从有序数组构建平衡BST并确保重复元素仅存在于右子树

如何从有序数组构建平衡BST并确保重复元素仅存在于右子树

我完全get到你的问题了!你现在的代码能把有序数组转成平衡BST,但碰到重复元素时,会有等于根节点的元素跑到左子树里(比如示例中层序遍历里的那个第三层的4),违反了你设定的规则——左子树所有节点必须小于当前节点,右子树必须大于等于当前节点。咱们来一步步把这个问题解决掉~

现有代码的问题分析

你的核心逻辑是每次取区间中间点作为根节点,递归构建左右子树。但当数组存在重复元素时,中间点左侧可能存在和根节点值相等的元素,这些元素会被递归到左子树,直接违反你的BST规则。比如示例数组里索引3的4,和根节点(索引4的4)值相等,却被放到了左子树的分支里,这就是问题的根源。

解决方案思路

要同时满足两个核心要求:

  1. BST规则:左子树全小于当前节点,右子树全大于等于当前节点;
  2. 平衡性:左右子树高度差不超过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);
}

关键代码解释

  1. findFirst函数:用二分法高效定位第一个等于目标值的元素,比线性扫描更适合大数组,时间复杂度为O(logn)。
  2. 修改后的toTree函数:
    • 先取区间中间值作为参考,保证根节点位置靠近区间中心;
    • 找到第一个等于该值的位置作为根节点,确保左区间所有元素都小于当前根节点值;
    • 右区间自然包含所有大于等于根节点值的元素,完全符合你的BST规则;
    • 根节点位置靠近区间中心,左右子树的区间长度差不会超过1,保证了树的平衡性。

结果验证

运行修改后的代码,你会发现:

  • 中序遍历结果和原数组完全一致,说明BST的排序逻辑正确;
  • 所有等于根节点的元素都出现在右子树分支中,完全符合你设定的规则;
  • 层序遍历结果显示树的结构是平衡的,左右子树高度差不超过1。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 08:49:31