前序遍历,中序遍历,后序遍历是如何还原一棵二叉树的?


二叉树的前中后三种遍历方式,各有各的特点。
我们可以通过前中后三种遍历方式中两种遍历方式的组合去还原一棵二叉树。
(无重复元素的树)

注意:
我们只能通过前中,中后两种方式去还原一棵确定的二叉树。

原因:
以前序遍历和中序遍历为例。
前序遍历是通过根左右的方式进行访问的,中序遍历时通过左根右的方式进行遍历的。
显而易见,前序遍历区分出根和左右节点,中序遍历区分出左节点和右节点,如此一来,根和左右节点的顺序一目了然,我们就可以通过代码来实现了。

前序遍历和中序遍历还原二叉树

在这里插入图片描述

思路(迭代)

  1. 构建根节点,创建一个索引指向中序遍历的首个元素,创建一个栈用来存储已经访问过的节点(此处的已经访问过指的是,将该节点的val进行赋值并存储,左右节点,并未完全构造),创建一个node指向栈顶元素。
  2. 对前序遍历的数组进行循环访问,当前栈顶元素 和中序遍历ord [index] (索引)值不相等时说明,当前的pre [i] 并不是这棵树的最左节点,于是创建新节点,为新节点赋值,并将新节点入栈,让栈顶元素的左子树指向这个新节点,并且更新栈顶元素。
  3. 当 栈顶元素的值 与 ord [index] 相等时说明,当前 前序遍历已经访问到了最左元素,于是循环将index后移并更新node然后进行弹栈操作,(注意 此处必须先更新栈顶元素node,然后进行弹栈操作,因为当跳出循环后,node的右孩子需要被指向。node的作用就是为了让节点和左右孩子进行相连)。
  4. 循环结束,返回根节点即可。

代码

class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if (!preorder.size()) {//条件判断
            return nullptr;
        }
        TreeNode* root = new TreeNode(preorder[0]);//建立根节点
        stack<TreeNode*> stk;//建立栈存储节点
        stk.push(root);//入根节点
        int inorderIndex = 0;//中序遍历的的元素下标

        for (int i = 1; i < preorder.size(); ++i) {//遍历前序遍历数组
            int preorderVal = preorder[i];//保存当前的元素值
            TreeNode* node = stk.top();//获取栈顶元素

            if (node->val != inorder[inorderIndex]) {//栈顶元素的值不等于当前中序遍历的元素
                node->left = new TreeNode(preorderVal);//可以模拟前序遍历进行对左孩子的访问
                stk.push(node->left);//入栈
            }
            else {//找到了最左节点
                while (!stk.empty() && stk.top()->val == inorder[inorderIndex]) {
                    //相等val 说明是最右节点 需要弹栈并++index
                    node = stk.top();
                    stk.pop();//可pop是因为在push的时候已经进行过访问了
                    //每个节点在push的时候就对自身的val进行了存储但是需要结合前中进行right和left的访问
                    ++inorderIndex;
                }
                node->right = new TreeNode(preorderVal);
                stk.push(node->right);
            }
        }
        return root;
    }
};

前序遍历和中序遍历还原二叉树

在这里插入图片描述

思路

通常从先序序列或者后序序列开始,根据不同遍历方法的规律,选择合适的节点构造树。
例如:先序序列的 第一个 节点是根节点,然后是它的左孩子,右孩子等等。
后序序列的 最后一个 节点是根节点,然后是它的右孩子,左孩子等等。
从先序/后序序列中找到根节点,根据根节点将中序序列分为左子树和右子树。从中序序列中获得的信息是:如果当前子树为空(返回 None),否则继续构造子树。

代码

/**
 * 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) {
        int pos = inorder.size()-1;//最后的有效位置
        return buildTree(postorder, pos, inorder, 0, pos);
    }

    TreeNode* buildTree(vector<int>& postorder, int& pos, vector<int>& inorder, int left, int right) {
        if (pos < 0 || left > right) {
            return 0;//终止条件
        }
        int p = left;
        while (p <= right && postorder[pos] != inorder[p]) {
            p++;
        }
        TreeNode* node = new TreeNode(postorder[pos]);
        if (p+1 <= right) {
            node->right = buildTree(postorder, --pos, inorder, p+1, right);
        }
        if (left <= p-1) {
            node->left = buildTree(postorder, --pos, inorder, left, p-1);   
        }
        return node;
    }
};

猜你喜欢

转载自blog.csdn.net/ifwecande/article/details/106899741
今日推荐