从前序与中序遍历序列构造二叉树

本文主要探讨了如何从给定的前序和中序遍历序列构造二叉树,针对LeetCode上的105和106题进行了解析。通过分而治之的策略,首先确定根节点,然后递归地构造左子树和右子树。文中还提供了源码示例,并强调在实现过程中避免不必要的拷贝操作以提高性能。

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

  进入来看我这篇文章的小伙伴应该大都是遇见了leetcode中的105. 从前序与中序遍历序列构造二叉树或是106. 从中序与后序遍历序列构造二叉树,其实这两个题是明显的同意思路,如果一个题目没有思路,可以看看别人的解答,然后把另外一个当作练习,这也是极好的。


原理讲解


  这两个题目的思路其实是和之前遍历二叉树的思路是一样的:分而治之
  在这样一个题目中,第一个重要的思路是前序(后序)在vector中的第一个值(最后一个值)是对应的一个父节点。

以下给出leetcode中的例子来帮助说明:

例子
不得不提的是这个题目有这样一个假设:你可以假设树中没有重复的元素。所以以下我会用数字代指节点。

  1. 在刚拿到这个题目时,我们可以根据前序vector得出判断:3是一个父节点(如果无法理解这一步的话,我们需要复习一下先序遍历)。
  2. 根据1的结论,我们再由中序队列得出3这个节点的左边是[9],右边是[15,20,7](如果无法理解这一步的话,我们需要复习一下中序遍历)。
  3. 由2的结论,我们能够知道3这个节点左边的节点有1个,右边有3个。由此得到在前序的情况下:3这个节点左边是[9],右边是[20,15,7]。

=> 到这里我们得到了:

前序:[9]    前序:[20,15,7]
中序:[9]   中序: [15,20,7]

  又回到了题目类似的条件。再如此循环,直到为空便可得出结果。

106. 从中序与后序遍历序列构造二叉树:源码

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        if(!inorder.size()) return nullptr;
        root = new TreeNode(NULL);
        _computeNode(inorder, postorder, root);
        return root;   
    }
private:
    void _computeNode(vector<int> inorder, vector<int> postorder, TreeNode* node) {
        if(inorder.size() == 0) {
            return;
        } else if (inorder.size() == 1) {
            node->val = inorder.back();
            return ;
        } else {
            int cur = postorder.back();
            node->val = cur;
            auto iter = find(inorder.begin(), inorder.end(), cur);
            if(iter == inorder.end()) return ;
            int rsize = inorder.end() - iter - 1;
            int lsize = iter - inorder.begin();
            vector<int> lv = vector<int>(inorder.begin(), iter);
            vector<int> rv =vector<int>(iter + 1, inorder.end());
            if(lv.size()) {
                node->left = new TreeNode(NULL);
                _computeNode(lv, vector<int>(postorder.begin(), postorder.begin() + lsize), node->left);
            }
            if(rv.size()) {
                node->right = new TreeNode(NULL);
                _computeNode(rv, vector<int>(postorder.end() - 1 - rsize, postorder.end() - 1), node->right);
            }
        }
    }
    TreeNode* root;
};

——————

105. 从前序与中序遍历序列构造二叉树:源码

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if(!preorder.size())
            return nullptr;
        root = new TreeNode(NULL);
        _buildTree(preorder.begin(), preorder.end(), inorder.begin(), inorder.end(), root);
        return root;
    }
private:
    TreeNode* root;
    void _buildTree(vector<int>::iterator preBegin, vector<int>::iterator preEnd,
                    vector<int>::iterator inBegin, vector<int>::iterator inEnd, TreeNode* node)
    {
        if(preEnd - preBegin == 0) return ;
        else if(preEnd - preBegin == 1) 
        {
          node->val = *preBegin;  
            return ;
        }else {
            int cur = *preBegin;
            node->val = cur;
            auto iter = find(inBegin, inEnd, cur);
            if(iter == inEnd) return ;
            int lsize = iter - inBegin;
            int rsize = inEnd - iter - 1;
            if(lsize) 
            {
                node->left = new TreeNode(NULL);
                _buildTree(preBegin + 1, preBegin + lsize + 1, inBegin, iter, node->left);
            }
            if(rsize) {
                node->right = new TreeNode(NULL);
                _buildTree(preBegin + lsize + 1, preEnd, iter + 1, inEnd, node->right);
            }
        }
    }
};



总结

  在106这个题目中,我用的是vector拷贝复制,性能消耗很大,但贵在思路看着更加清晰,跑起来88ms,比一些python的解决方案还要慢。在105这个题目中,我改正了这个错误,改用迭代器,28ms左右。可见拷贝这一行为的性能消耗之大,谨记。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值