题意整理:

题目给出一颗二叉树的前序遍历以及中序遍历,要求得到这颗二叉树的右视图。右视图简单的理解,既在树的右侧能够看到的数的第一个元素。更专业的说,即为树的每一层的最右侧元素。

方法一:递归+层序遍历

核心思想:

可以先使用递归,从先序遍历和中序遍历中重构树,再使用层次遍历求出右视图。
重构树:对一颗二叉树,其前序遍历的顺序为:
1.遍历根节点 2.递归地遍历左子树 3.递归地遍历右子树。
其中序遍历的顺序为:
1.递归地遍历左子树. 2遍历根节点 3.递归地遍历右子树。
在递归遍历子树的过程中,可以将这颗子树看成一颗新的树按以上步骤进行重建,所以重构可以通过简单的的递归实现。
对于前序遍历,其第一个元素就是根节点,故只要在中序遍历中定位到根节点,就可以知道左子树和右子树中的节点数目。而同一颗子树的前序遍历和中序遍历的长度相同,所以将此结果对应到前序遍历上,就可以对数组进行划分后递归,得到最终结果
图示:图片说明
层序遍历得到右视图:将树重建之后,只需要对数进行层序遍历,每次取出一整层且记录每层最后一个元素,遍历完成后便得到结果。层序遍历可以借助队列实现。

核心代码:

class Solution {
public:
    //构建的树的结构
    struct Node{
        int val;
        Node* left;
        Node* right;
        Node() : val(0), left(nullptr), right(nullptr) {}
        Node(int x) : val(x), left(nullptr), right(nullptr) {}
    };
    //建树函数
    Node* build(vector<int>& xianxu, vector<int>& zhongxu, int L1, int R1, int L2, int R2) {
        if(L1 > R1 || L2 > R2) return nullptr;
        int val = xianxu[L1];//以前序遍历的第一个元素为根
        int p = L2;
        while(zhongxu[p] != val) ++p;//寻找中序遍历中根节点位置
        int jump = p - L2;
        Node* root = new Node(val);
        //递归建立左右子树
        root->left = build(xianxu, zhongxu, L1 + 1, L1 + jump, L2, p - 1);
        root->right = build(xianxu, zhongxu, L1 + jump + 1, R1, p + 1, R2);
        return root;
    }
    vector<int> solve(vector<int>& xianxu, vector<int>& zhongxu) {
        Node* root = build(xianxu, zhongxu, 0, xianxu.size() - 1, 0, zhongxu.size() - 1);
        vector<int> ans;
        if(root == nullptr) {
            return ans;
        }
        //层序遍历得到右视图
        queue<Node*> q;
        q.push(root);
        while(!q.empty()) {
            int n = q.size(); //当前层的长度
            for(int i = 0; i < n; ++i) {
                Node* t = q.front(); 
                q.pop();
                if(i == n - 1) ans.push_back(t->val);//每一层最后一个节点
                if(t->left != nullptr) q.push(t->left);
                if(t->right != nullptr) q.push(t->right);
            }
        }
        return ans;
    }

复杂度分析:

时间复杂度:, 是树的节点数。建树时,最坏情况的树为左偏树,此时每次遍历都需要找到最右边找到中序遍历的根节点,进行 次遍历,故复杂度为 。层序遍历时每个节点最多访问一次,复杂度为
空间复杂度:,既当树退化为链表时所需的栈空间,以及层序遍历时的队列空间,两者都不会超过树的节点数

方法二:在递归中直接得到右视图

核心思想:

题目要求的只是返回右视图,所以可以考虑不对二叉树进行实际的重建。
同方法一,在通过找到前序遍历的首元素在中序遍历中的位置进行递归时,可以在递归时同时记录本节点处于的高度,如果高度不存在于答案数组,进行填入;否则说明本节点比答案数组中的同高度节点位于同一层且更靠右,可以进行更新。(因为递归是先左子树后右子树,保证了后遍历到的同高度节点是靠右侧的节点)

核心代码:

class Solution {
public:
    vector<int> ans;//存储答案

    void find(vector<int>& xianxu, vector<int>& zhongxu, int L1, int R1, int L2, int R2, int height) {
        if(L1 > R1 || L2 > R2) return;
        int val = xianxu[L1];//以先序遍历的第一个元素为根
        int p = L2;
        while(zhongxu[p] != val) ++p;//寻找中序遍历中根节点位置
        int jump = p - L2;
        if(height > ans.size()) {
            ans.push_back(val);
        } else {
            ans[height - 1] = val;//更新最新的最右节点
        }
        //递归寻找左右子树
        find(xianxu, zhongxu, L1 + 1, L1 + jump, L2, p - 1, height + 1);
        find(xianxu, zhongxu, L1 + jump + 1, R1, p + 1, R2, height + 1);
    }
    vector<int> solve(vector<int>& xianxu, vector<int>& zhongxu) {
        find(xianxu, zhongxu, 0, xianxu.size() - 1, 0, zhongxu.size() - 1, 1);
        return ans;
    }
};

复杂度分析:

时间复杂度:, 是树的节点数。同方法一,当最坏情况时,树为左偏树,此时每次遍历都需要找到最右边找到中序遍历的根节点,进行 次遍历,故复杂度为
空间复杂度:,既当树退化为链表时所需的栈空间。