[剑指Offer]:重建二叉树(P:代码测试)

123 阅读4分钟

文章目录


题目描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

例如,给出

  • 前序遍历 preorder = [3,9,20,15,7]
  • 中序遍历 inorder = [9,3,15,20,7]

返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

题解思路

方法一:迭代

在先序遍历中,前后紧邻的两个节点a和b,在树中的位置有三种情况:

  1. b是a的左子树节点(父子关系),对应a有左子树
  2. b是a的右子树节点(父子关系),对应a没有左子树,但有右子树
  3. b是a的某个有右子树的祖父节点,对应a既没有左子树,也没有右子树

因为在前序遍历中,a的祖父节点和祖父节点的左子树绝不可能在a的后面。

在中序遍历中,前后紧邻的两个节点a和b,在树中的位置有两种情况:

  1. b是a的右子树中的最左侧节点,对应a有右子树(在a右子树只有一个节点的情况下,退化为b是a的右节点)
  2. a是b的左子树中的最右侧节点,对应a没有右子树(在b的左子树只有一个节点的情况下,退化为a是b的左节点)

代码实现:

/**
 * 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() != preorder.size() || preorder.size() == 0) return nullptr;
        TreeNode* root = new TreeNode(preorder[0]); // 创建根节点,并标记
        TreeNode* cur = root;  // 正在确定位置的节点

        stack<TreeNode*> s;
        for(int i = 1, j = 0; i < preorder.size(); ++i) {
            // 有左子树的情况,一直沿左子树深入,并将沿途节点放入栈中
            if (cur->val != inorder[j]) {   // inorder[j]代表最左节点(除已经确定左子树的节点)
                cur->left = new TreeNode(preorder[i]);
                s.push(cur);
                cur = cur->left;
            } 
            else {  // 没有左子树的情况
                j++;
                while (!s.empty() && s.top()->val == inorder[j]) {  // 栈顶是其父节点,判断有无右子树
                    cur = s.top();  // 没有右子树,就追溯到有右节点的祖父节点
                    s.pop();
                    j++;
                }
                cur->right = new TreeNode(preorder[i]);  // preorder[i]即当前节点的右子树节点,并且下次从右子树开始遍历
                cur = cur->right;
            }
        }
        return root;
    }
};

方法二:递归

  在二叉树的前序遍历序列中,第一个数字总是树的根结点的值。但在中序遍历序列中,根结点的值在序列的中间,左子树的结点的值位于根结点的值的左边,而右子树的结点的值位于根结点的值的右边。因此我们需要扫描中序遍历序列,才能找到根结点的值。

  前序遍历序列的第一个数字1就是根结点的值。扫描中序遍历序列,就能确定根结点的值的位置。根据中序遍历特点,在根结点的值1前面的3个数字都是左子树结点的值,位于1后面的数字都是右子树结点的值。

  在二叉树的前序遍历和中序遍历的序列中确定根结点的值、左子树结点的值和右子树结点的值的步骤如下图所示:

在这里插入图片描述

分别找到了左、右子树的前序遍历序列和中序遍历序列,我们就可以用同样的方法分别去构建左右子树。换句话说,这是一个递归的过程。

代码实现

BinaryTreeNode* ConstructCore(int* startPreorder, int* endPreorder, int* startInorder, int* endInorder){
    BinaryTreeNode* root = new BinaryTreeNode();
    int rootValue = startPreorder[0];
    root->m_value = rootValue;
    root->m_right = root->m_left = NULL;

    if(startPreorder == endPreorder){
        if(startInorder == endInorder && *startPreorder == *startInorder) return root;
        else {
            try{
                throw std::exception();
            }
            catch(...){
                cout << "Invalid input." << endl;
            }
        }
    }
    else{
        int* rootInorder = startInorder;
        while(rootInorder < endInorder && *rootInorder != rootValue) ++rootInorder;

        if(rootInorder == endInorder && *rootInorder != rootValue) {
            try{
                throw std::exception();
            }
            catch(...){
                cout << "Invalid input." << endl;
            }
        }
        int leftLength = rootInorder - startInorder;
        int* leftPreorderEnd = startPreorder + leftLength;
        if(leftLength > 0){
            // 构建左子树
            root->m_left = ConstructCore(startPreorder + 1, leftPreorderEnd, startInorder, rootInorder-1);
        }
        if(leftLength < endPreorder - startPreorder){
            // 构建右子树
            root->m_right = ConstructCore(leftPreorderEnd + 1, endPreorder, rootInorder+1, endInorder);
        }
    }
    return root;
}

BinaryTreeNode* Construct(int* preorder, int* inorder, int length){
    if(preorder == nullptr || inorder == nullptr || length <= 0) return nullptr;
    return ConstructCore(preorder, preorder+length-1, inorder, inorder+length-1);
}

代码测试:

#include <iostream>
#include <queue>
using namespace std;

struct BinaryTreeNode{
    int m_value;
    BinaryTreeNode * m_left;
    BinaryTreeNode * m_right;
};

BinaryTreeNode* ConstructCore(int* startPreorder, int* endPreorder, int* startInorder, int* endInorder){
    BinaryTreeNode* root = new BinaryTreeNode();
    int rootValue = startPreorder[0];
    root->m_value = rootValue;
    root->m_right = root->m_left = NULL;

    if(startPreorder == endPreorder){
        if(startInorder == endInorder && *startPreorder == *startInorder) return root;
        else {
            try{
                throw std::exception();
            }
            catch(...){
                cout << "Invalid input." << endl;
            }
        }
    }
    else{
        int* rootInorder = startInorder;
        while(rootInorder < endInorder && *rootInorder != rootValue) ++rootInorder;

        if(rootInorder == endInorder && *rootInorder != rootValue) {
            try{
                throw std::exception();
            }
            catch(...){
                cout << "Invalid input." << endl;
            }
        }
        int leftLength = rootInorder - startInorder;
        int* leftPreorderEnd = startPreorder + leftLength;
        if(leftLength > 0){
            // 构建左子树
            root->m_left = ConstructCore(startPreorder + 1, leftPreorderEnd, startInorder, rootInorder-1);
        }
        if(leftLength < endPreorder - startPreorder){
            // 构建右子树
            root->m_right = ConstructCore(leftPreorderEnd + 1, endPreorder, rootInorder+1, endInorder);
        }
    }
    return root;
}

BinaryTreeNode* Construct(int* preorder, int* inorder, int length){
    if(preorder == nullptr || inorder == nullptr || length <= 0) return nullptr;
    return ConstructCore(preorder, preorder+length-1, inorder, inorder+length-1);
}

// 层序遍历
void BinaryTreeLevelOrder(BinaryTreeNode * root){
    queue<BinaryTreeNode*> qu;
    qu.push(root);
    BinaryTreeNode* cur;

    while(!qu.empty()){
        cur = qu.front();
        cout << cur->m_value << ' ';
        if(cur->m_left) qu.push(cur->m_left);
        if(cur->m_right) qu.push(cur->m_right);
        qu.pop();
    }
}

int main(){
    /*
    int preorder[] = {1, 2, 4, 7, 3, 5, 6, 8};  // 前序遍历
    int inorder[] = {4, 7, 2, 1, 5, 3, 8, 6};   // 中序遍历
    BinaryTreeNode * root = Construct(preorder, inorder, 8);*/
    int preorder[] = {1, 2, 3, 4, 5};
    int inorder[] = {1, 2, 3, 4, 5};
    BinaryTreeNode * root = Construct(preorder, inorder, 5);

    BinaryTreeLevelOrder(root);
    return 0;
}

代码实现图:

在这里插入图片描述
在这里插入图片描述


如有不同见解,欢迎留言讨论~~~