题意整理:
题目给出一颗二叉树的前序遍历以及中序遍历,要求得到这颗二叉树的右视图。右视图简单的理解,既在树的右侧能够看到的数的第一个元素。更专业的说,即为树的每一层的最右侧元素。
方法一:递归+层序遍历
核心思想:
可以先使用递归,从先序遍历和中序遍历中重构树,再使用层次遍历求出右视图。
重构树:对一颗二叉树,其前序遍历的顺序为:
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;
}
};复杂度分析:
时间复杂度:,
是树的节点数。同方法一,当最坏情况时,树为左偏树,此时每次遍历都需要找到最右边找到中序遍历的根节点,进行
次遍历,故复杂度为
。
空间复杂度:,既当树退化为链表时所需的栈空间。



京公网安备 11010502036488号