首页 > 其他分享 >【LeetCode二叉树#10】从中序与后序遍历序列构造二叉树

【LeetCode二叉树#10】从中序与后序遍历序列构造二叉树

时间:2023-02-27 20:44:14浏览次数:61  
标签:10 遍历 后序 中序 vector 二叉树 数组 LeetCode postorder

力扣题目链接(opens new window)

根据一棵树的中序遍历与后序遍历构造二叉树。

注意: 你可以假设树中没有重复的元素。

例如,给出

中序遍历 inorder = [9,3,15,20,7] 后序遍历 postorder = [9,15,7,20,3] 返回如下的二叉树:

106. 从中序与后序遍历序列构造二叉树1

思路

题目给了目标二叉树的中序遍历(左中右)和后序遍历(左右中)结果数组

那么我们可以通过后序遍历的结果数组的最后一个值确定该二叉树的根节点

有了根节点,下一步就要确定根节点两侧的树的节点,这时就需要使用根节点分别在中序和后序结果数组中分割(一定是先中序再后序)

按顺序在中序遍历和后序遍历的结果数组中使用该值进行数组分割(注意,这里的左右是针对当前所分割的数组来说的)

  • 先分割中序遍历的结果数组,得到中序左数组和中序右数组
  • 再分割后序遍历的结果数组,得到后序左数组和后序右数组

完成分割之后,再使用递归对左右区间进行相同的处理,直到最后碰到叶子节点

图示如下:

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

上图例子中,先通过后序遍历数组([9,15,7,20,3])的最后一个数确认根节点(中间节点) 3

先在中序遍历数组([9,3,15,20,7])中按 3 进行分割,得到左边部分([9])和右边部分([15,20,7]),即中序左数组和中序右数组

然后在后序遍历数组([9,15,7,20,3])中进行分割

因为 3 在后序遍历数组的最后一位,所以不能用来做分割点位,并且也不能继续保留了

那怎么分割后序数组呢?

这里有一个规律:中序遍历数组和后序遍历数组在整体大小上永远是相同的

我们既然已经分割好了中序遍历数组,那么就可以根据左边中序遍历数组(不能是右边)的大小来对后序遍历数组进行分割

后序遍历数组([9,15,7,20,3])分割好后得到左边部分([9])和右边部分([15,7,20]),即后序左数组和后序右数组

此时我们需要进行递归对 左中序/后序 和 右中序/后序 进行处理,处理逻辑同上(也是使用后序数组确定中节点,分割中序数组,再通过中序数组分割后序数组)

代码

代码实现的步骤一共分以下几步:

  • 第一步:如果数组大小为零的话,说明是空节点了。
  • 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
  • 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
  • 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
  • 第五步:切割后序数组,切成后序左数组和后序右数组
  • 第六步:递归处理左区间和右区间

本题的代码比较多,可以先把整体框架写出来再补充

框架
//递归函数的返回值应 
TreeNode* traversal(vector<int>& inorder, vector<int>& postorder){
    //确认数组是否为0
    if(postorder.size() == 0) return NULL;
    
    //后序遍历数组的最后一个数,即根节点
    int rootValue = postorder[postorder.size() - 1];
    //创建根节点
    TreeNode* root = new TreeNode(rootValue);
    
    //如果当前postorder只有一个值,那根节点就是叶子节点,可直接返回
    if(postorder.size() == 1) return root;
    
    //遍历中序遍历数组,寻找切割点
    //注意,是在中序遍历数组找出与rootValue值相同的元素的 下标 
    int cutIndex;
    for(cutIndex = 0; cutIndex < inorder.size(); ++cutIndex){
        //遇到目标值就结束遍历
        if(inorder[cutIndex] == rootValue) break;
    }
    
    // 第四步:切割中序数组,得到 中序左数组和中序右数组
    // 第五步:切割后序数组,得到 后序左数组和后序右数组
    
    //第六步,调用递归处理root两侧的节点
    root->left = traversal(中序左数组, 后序左数组);
    root->right = traversal(中序数右组, 后序右数组); 
}
切割数组

定义vector保存切割中序/后序数组的结果

//递归函数的返回值应 
TreeNode* traversal(vector<int>& inorder, vector<int>& postorder){
    ...
    
    //遍历中序遍历数组,寻找切割点
    //注意,是在中序遍历数组找出与rootValue值相同的元素的 下标 
    int cutIndex;
    for(cutIndex = 0; cutIndex < inorder.size(); ++cutIndex){
        //遇到目标值就结束遍历
        if(inorder[cutIndex] == rootValue) break;
    }
    
    // 第四步:切割中序数组,得到 中序左数组和中序右数组
    // 左闭右开区间:[0, delimiterIndex)
    vector<int> leftInorder(inorder.begin(), inorder.begin() + cutIndex);
    vector<int> rightInorder(inorder.begin() + cutIndex + 1, inorder.end());
    // 第五步:切割后序数组,得到 后序左数组和后序右数组
    // 先把后序数组里面的最后一个元素舍弃,,那个节点已经作为根节点了
    postorder.resize(postorder.size() - 1);
    vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
    vector<int> rightPostorder(postorder.begin() + leftInorder.size() + 1, postorder.end());
    ...
}

之后再调用递归,按照相同逻辑重复处理分割出来的区间

完整代码
class Solution {
public:
    //递归函数的返回值应 
    TreeNode* traversal(vector<int>& inorder, vector<int>& postorder){
        //确认数组是否为0
        if(postorder.size() == 0) return NULL;

        //后序遍历数组的最后一个数,即根节点
        int rootValue = postorder[postorder.size() - 1];
        //创建根节点
        TreeNode* root = new TreeNode(rootValue);

        //如果当前postorder只有一个值,那根节点就是叶子节点,可直接返回
        if(postorder.size() == 1) return root;

        //遍历中序遍历数组,寻找切割点
        //注意,是在中序遍历数组找出与rootValue值相同的元素的 下标 
        int cutIndex;
        for(cutIndex = 0; cutIndex < inorder.size(); ++cutIndex){
            //遇到目标值就结束遍历
            if(inorder[cutIndex] == rootValue) break;
        }

        // 第四步:切割中序数组,得到 中序左数组和中序右数组
        // 左闭右开区间:[0, delimiterIndex)
        vector<int> leftInorder(inorder.begin(), inorder.begin() + cutIndex);
        // [delimiterIndex + 1, end)
        // 这里加1是为了跳过根节点3
        vector<int> rightInorder(inorder.begin() + cutIndex + 1, inorder.end());
        // 第五步:切割后序数组,得到 后序左数组和后序右数组
        // 先把后序数组里面的最后一个元素舍弃,那个节点已经作为根节点了
        // 依然左闭右开,注意这里使用了左中序数组大小作为切割点
        // [0, leftInorder.size)
        postorder.resize(postorder.size() - 1);
        vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
        // [leftInorder.size(), end)
        //这里不用加1是因为3已经在前面被删除了
        vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());
        
        /*// 以下为日志
        cout << "----------" << endl;

        cout << "leftInorder :";
        for (int i : leftInorder) {
            cout << i << " ";
        }
        cout << endl;

        cout << "rightInorder :";
        for (int i : rightInorder) {
            cout << i << " ";
        }
        cout << endl;

        cout << "leftPostorder :";
        for (int i : leftPostorder) {
            cout << i << " ";
        }
        cout << endl;
         cout << "rightPostorder :";
        for (int i : rightPostorder) {
            cout << i << " ";
        }
        cout << endl;*/
        

        //第六步,调用递归处理root两侧的节点
        root->left = traversal(leftInorder, leftPostorder);
        root->right = traversal(rightInorder, rightPostorder);

        return root;
    }

    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        //判断所给数组是否为空
        if (inorder.size() == 0 || postorder.size() == 0) return NULL;
        return traversal(inorder, postorder);
    }
};
注意点

0、先把框架写了再填内容

1、分割中序数组时,记得不要把3再加进来

2、别忘了vector的构造方式

void test01()
{
	vector<int> v1; //无参构造
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	printVector(v1);

	vector<int> v2(v1.begin(), v1.end());
	printVector(v2);

	vector<int> v3(10, 100);
	printVector(v3);
	
	vector<int> v4(v3);
	printVector(v4);
}

标签:10,遍历,后序,中序,vector,二叉树,数组,LeetCode,postorder
From: https://www.cnblogs.com/DAYceng/p/17161824.html

相关文章

  • BUUCTF—CRYPTO 1—10
    BUUCTF—CRYPTO1—101、MD5题目:e00cf25ad42683b3df678c61f42c6bda解析:看题目就知道是MD5加密,直接上在线解码网站解码,答案是:flag2、BASE64题目:ZmxhZ3tUSEVfRkxBR19PRl......
  • #yyds干货盘点# LeetCode面试题:串联所有单词的子串
    1.简述:给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串长度相同。 s 中的串联子串是指一个包含 words 中所有字符串以任意顺序排列连接起来的......
  • 2022-10-27-各种Normallize的区别
    layout:posttitle:CS231N-课后思考后笔记subtitle:CS231N-课后思考后笔记description:CS231N-课后思考后笔记date:2022-10-26categories:deep......
  • 2022-10-26-CS231N-课后思考后笔记
    layout:posttitle:CS231N-课后思考后笔记subtitle:CS231N-课后思考后笔记description:CS231N-课后思考后笔记date:2022-10-26categories:deep......
  • LeetCode 79. 单词搜索(/dfs)
    原题解题目约束题解classSolution{public:boolcheck(vector<vector<char>>&board,vector<vector<int>>&visited,inti,intj,string&s,intk)......
  • LeetCode 周赛 334,在算法的世界里反复横跳
    本文已收录到AndroidFamily,技术和职场问题,请关注公众号[彭旭锐]提问。大家好,我是小彭。今天是LeetCode第334场周赛,你参加了吗?这场周赛考察范围比较基础,整体难度......
  • LeetCode 78. 子集(/)
    原题解题目约束题解解法一classSolution{public:vector<int>t;vector<vector<int>>ans;vector<vector<int>>subsets(vector<int>&nums)......
  • #yyds干货盘点# LeetCode面试题:下一个排列
    1.简述:整数数组的一个排列 就是将其所有成员以序列或线性顺序排列。例如,arr=[1,2,3],以下这些都可以视作arr的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1]。整数数组的......
  • #yyds干货盘点# LeetCode面试题:最长有效括号
    1.简述:给你一个只包含'(' 和')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。 示例1:输入:s="(()"输出:2解释:最长有效括号子串是"()"示例2:输入:s=")()())"......
  • #yyds干货盘点# LeetCode程序员面试金典:平分正方形
    题目:给定两个正方形及一个二维平面。请找出将这两个正方形分割成两半的一条直线。假设正方形顶边和底边与x轴平行。每个正方形的数据square包含3个数值,正方形的左下顶点坐......