leetcode还原二叉搜索树

本文介绍了一种修复二叉搜索树中元素错误排序的方法,通过遍历树并使用交换操作来纠正错误,确保树的性质得以恢复。代码示例展示了创建、显示二叉树以及执行修复过程的完整流程。
#include <stdio.h>
#include <iostream>
#include <algorithm>
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {
    }
};
TreeNode *root;
class Solution {
public:
    void create_bt(TreeNode *&root) {
        int e;
        std::cin >> e;
        if (e != -1) {
            root = (TreeNode *)malloc(sizeof(TreeNode));
            root->val = e;
            root->left = nullptr;
            root->right = nullptr;
            create_bt(root->left);
            create_bt(root->right);
        }
    }
    void show_bt(TreeNode *root) {
        if (!root) {
            return;
        }
        std::cout << root->val << std::endl;
        show_bt(root->left);
        show_bt(root->right);
    }
    void transform_tree(TreeNode *root) {
        if (!root) {
            return;
        }
        transform_tree(root->left);
        if (pre_node && pre_node->val > root->val) {
            std::swap(pre_node->val, root->val);
            change = true;
            return;

        }
        pre_node = root;
        transform_tree(root->right);
    }
    void recoverTree(TreeNode *root) {
        do {
            pre_node = nullptr;
            change = false;
            transform_tree(root);
        } while (change);
    }
private:
    TreeNode *pre_node;
    bool change;
};
int main() {
    Solution s;
    s.create_bt(root);
    s.show_bt(root);
    s.recoverTree(root);
    s.show_bt(root);
    
    return 0;
}

<think>嗯,用户问的是如何通过前序遍历构建二叉搜索树。首先,我得回忆一下二叉搜索树(BST)的性质,前序遍历的特点,以及如何结合这两者来重建树。BST的中序遍历是有序的,但这里给的是前序遍历,所以需要找到方法将前序遍历的数组转换成BST的结构。 根据前序遍历的定义,根节点是数组的第一个元素。接下来的元素中,比根小的都是左子树的节点,比根大的都是右子树的节点。这个分割点很重要,因为一旦找到第一个比根大的元素,就可以确定左右子树的分界了。比如示例中的输入是[8,5,1,7,10,12],根是8,然后后面的元素中5、1、7都比8小,属于左子树,而10、12是右子树。 接下来,我需要考虑如何递归地处理左右子树。对于左子树的部分,同样是前序遍历的结果,所以左子树的根节点就是第二个元素,这里是5,然后继续分割左右子树。右子树的根是10,依此类推。这个过程需要递归地应用,直到处理完所有元素。 但用户提供的引用中有不同的实现方法。比如引用[2]中的C语言代码,通过遍历数组找到第一个大于当前根节点的元素作为右子树的起点,然后递归构建左右子树。而引用[4]的Java代码则是逐个插入节点,类似于常规的BST插入方法。这两种方法的时间复杂度可能不同,递归分割的方法时间复杂度是O(n log n)到O(n²)之间,取决于树的结构,而逐个插入的方法在最坏情况下可能达到O(n²),比如当输入是升序或降序时。 用户可能需要的是高效的算法,所以应该选择递归分割的方法,因为它在平均情况下是O(n log n),并且更符合前序遍历的特性。而逐个插入的方法虽然简单,但效率可能不高。另外,递归分割的方法更直接地利用前序遍历的结构,因为每个子数组对应左或右子树的前序遍历结果。 然后,我需要验证示例是否正确。比如输入[8,5,1,7,10,12],按照递归分割的方法,根是8,左子树是5、1、7,右子树是10、12。左子树的根是5,它的左子树是1,右子树是7;右子树的根是10,右子树是12。这样构建的树结构符合示例的输出。而如果使用逐个插入的方法,当插入到10时,由于根是8,10作为右子节点插入,接着插入12到右子树的右节点,同样能得到正确结果,但两种方法生成的树结构是否相同?可能不同,比如当存在多个可能的BST时,题目中保证只有一个正确答案,但根据BST的定义,可能不同的插入顺序会导致不同的树结构,但题目说明总能找到符合要求的树,所以两种方法可能都是可行的,但题目可能期望的是根据前序遍历正确分割左右子树的方法,即递归分割法。 需要指出两种方法的区别,并推荐正确的方法。例如,逐个插入的方法可能生成不同的树结构,但题目可能要求严格按照前序遍历的顺序来构建,因此必须找到左右子树的分界点,然后递归处理。这样才能保证生成的树的结构与前序遍历一致。例如,在示例中,左子树的前序遍历是[5,1,7],因此根是5,然后是左子节点1,右子节点7,而右子树的前序遍历是[10,12],根是10,右子节点12。这样构建的树结构正确。 接下来,可能需要解释代码实现的关键步骤。比如,在递归函数中,参数是数组的起始和结束索引,每次确定根节点后,找到分割点,然后递归处理左子树和右子树。同时,处理边界条件,比如当起始等于结束时返回空指针。 此外,需要考虑时间复杂度的优化。例如,分割点的查找可以通过线性扫描,每次递归需要O(n)的时间,总的时间复杂度是O(n²)(最坏情况,比如链表结构的树),或者优化为O(n)的方法,比如使用单调栈或者预先确定分割点的位置。但在LeetCode题解中,通常线性扫描的方法已经足够,因为题目可能接受这样的解法。 最后,需要总结步骤:确定根节点,分割左右子树,递归构建。同时,比较不同方法的优缺点,并给出示例代码。</think>### 如何通过前序遍历构建二叉搜索树 #### 核心原理 二叉搜索树(BST)的前序遍历数组满足以下特征: 1. **根节点**是数组的第一个元素 2. **左子树**的所有节点值均小于根节点值,构成连续的左段 3. **右子树**的所有节点值均大于根节点值,构成连续的右段 例如前序遍历数组 $[8,5,1,7,10,12]$ 中: - 根节点为 $8$ - 左子树区间为 $[5,1,7]$(均小于 $8$) - 右子树区间为 $[10,12]$(均大于 $8$) #### 实现步骤 1. **确定根节点**:前序遍历数组的首元素即为当前子树的根节点。 2. **划分左右子树**:从根节点后的第一个元素开始遍历,找到第一个大于根节点值的元素,作为右子树的起点。 3. **递归构建子树**:左子树区间为 `[根索引+1, 分割点-1]`,右子树区间为 `[分割点, 结束索引]`。 #### 代码实现(Python) ```python class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def bst_from_preorder(preorder): def build(start, end): if start >= end: return None root_val = preorder[start] root = TreeNode(root_val) # 寻找右子树起始点 split = start + 1 while split < end and preorder[split] < root_val: split += 1 # 递归构建左右子树 root.left = build(start + 1, split) root.right = build(split, end) return root return build(0, len(preorder)) ``` #### 复杂度分析 - **时间复杂度**:最坏情况 $O(n^2)$(如链表状树),平均情况 $O(n \log n)$ - **空间复杂度**:递归栈深度 $O(h)$,其中 $h$ 为树高 #### 示例验证 输入 `preorder = [8,5,1,7,10,12]` 时: 1. 根节点为 $8$ 2. 左子树区间 `[5,1,7]`,右子树区间 `[10,12]` 3. 递归构建后得到如下结构: ``` 8 / \ 5 10 / \ \ 1 7 12 ``` #### 方法对比 | 方法 | 时间复杂度 | 空间复杂度 | 适用场景 | |------------|------------|------------|------------------------------| | 递归分割法 | $O(n^2)$ | $O(h)$ | 直接利用前序遍历特性 | | 逐个插入法 | $O(n^2)$ | $O(n)$ | 简单实现但可能生成不同结构树 | 递归分割法更符合前序遍历的语义,能准确还原原始树结构[^1][^2]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值