题目

请根据二叉树的前序遍历,中序遍历恢复二叉树,并打印出二叉树的右视图

数据范围: 0≤n≤100000≤n≤10000
要求: 空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)

如输入[1,2,4,5,3],[4,2,5,1,3]时,通过前序遍历的结果[1,2,4,5,3]和中序遍历的结果[4,2,5,1,3]可重建出以下二叉树:

C++代码实现:

TreeNode* buildTree(vector<int> & xianxu, int l1, int r1, vector<int> &zhongxu, int l2, int r2){if (l1 > r1 || l2 > r2) return NULL;TreeNode* root = new TreeNode(xianxu[l1]);int rootIndex = 0;for (int i = l2; i <= r2; ++i){if (zhongxu[i] == xianxu[l1]){rootIndex = i;break;}}int leftsize = rootIndex - l2;int rightsize = r2 - rootIndex;root->left = buildTree(xianxu, l1+1, l1+leftsize, zhongxu, l2, l2+leftsize-1);root->right = buildTree(xianxu, l1+leftsize+1, r1, zhongxu, rootIndex+1, r2);return root;}vector<int> rightSideView(TreeNode *root){unordered_map<int, int> mp;int max_depth = -1;stack<TreeNode*> nodes;stack<int> depth;nodes.push(root);depth.push(0);while (!nodes.empty()){TreeNode* node = nodes.top();nodes.pop();int depth1 = depth.top();depth.pop();if (node != NULL)   {max_depth = max(depth1, max_depth);if (mp.find(depth1) == mp.end()){mp[depth1] = node->val;}nodes.push(node->left);nodes.push(node->right);depth.push(depth1+1);depth.push(depth1+1);} }vector<int> res;for (int i = 0; i <= max_depth; ++i){res.push_back(mp[i]);}return res;}vector<int> solve(vector<int>& preOrder, vector<int>& inOrder) {vector<int> res;if (preOrder.size() == 0) return res;TreeNode* root = buildTree(preOrder, 0, preOrder.size()-1, inOrder, 0, inOrder.size()-1);return rightSideView(root);}

右视图是啥?

右视图就是「站在二叉树右边看过去,能看到的每个层的最右边节点」。比如下面这个树:

    1        (第0层,看到1)/   \2     3     (第1层,看到3)
/     / \
4     5   6   (第2层,看到6)

右视图结果就是 [1,3,6]—— 核心是要拿到「每一层的第一个遇到的最右节点」。

为什么用栈?栈在这里的作用

栈是 “先进后出” 的容器,适合深度优先遍历(先往深了走,走到头再回头)。这里用了 两个栈

  • nodes 栈:存要遍历的二叉树节点;
  • depth 栈:存对应节点的 “深度”(根节点深度是 0,子节点比父节点深 1)。
    两个栈是 “同步操作” 的 —— 弹出一个节点,就对应弹出它的深度;压入节点时,也对应压入它的深度。

逐行拆解代码逻辑(结合例子看更清楚)

咱们就用上面的树 [1,2,3,4,5,6] 当例子,一步一步看栈和数据的变化。

初始状态

一开始,把根节点 1 和它的深度 0 分别压入两个栈:

  • nodes 栈:[1](栈顶是 1)
  • depth 栈:[0](栈顶是 0)
  • mp(存 “深度→最右节点值”):空
  • max_depth(记录最大深度):-1
进入循环:while (!nodes.empty ())(栈不空就继续)

循环的核心逻辑是:弹出栈顶节点→判断是不是有效节点→处理有效节点→压入它的子节点

第一步:弹出节点和对应深度
TreeNode* node = nodes.top();  // 取nodes栈顶节点(一开始是1)
nodes.pop();                   // 把1从nodes栈弹出,nodes现在空了
int depth1 = depth.top();      // 取depth栈顶深度(0)
depth.pop();                   // 把0从depth栈弹出,depth现在空了

此时:node=1depth1=0

第二步:判断节点是否有效(非 NULL)
if (node != NULL)  // 1不是NULL,进入处理
{// 1. 更新最大深度:当前深度0比初始的-1大,所以max_depth=0max_depth = max(depth1, max_depth);  // max(0,-1)=0// 2. 记录当前深度的最右节点(关键!)if (mp.find(depth1) == mp.end())  // 查mp里有没有深度0的记录?一开始没有{mp[depth1] = node->val;  // 把深度0和值1存进去,mp现在是 {0:1}}// 3. 压入当前节点的左、右子节点(重点!栈是先进后出,所以先压左,再压右)nodes.push(node->left);   // 压入1的左子节点2 → nodes栈:[2]nodes.push(node->right);  // 再压入1的右子节点3 → nodes栈:[2,3](栈顶是3)// 4. 同步压入子节点的深度(父节点深度+1=0+1=1)depth.push(depth1+1);  // 压入2的深度1 → depth栈:[1]depth.push(depth1+1);  // 再压入3的深度1 → depth栈:[1,1](栈顶是1)
}

这一步结束后

  • nodes 栈:[2,3](栈顶是 3)
  • depth 栈:[1,1](栈顶是 1)
  • mp{0:1}
  • max_depth:0
第三步:继续循环(nodes 栈不空,处理栈顶的 3)

重复第一步:弹出节点和深度

  • node = nodes.top() → 3;nodes.pop() → nodes 变成 [2]
  • depth1 = depth.top() → 1;depth.pop() → depth 变成 [1]

判断 3 非 NULL,进入处理:

  1. 更新 max_depth:max (1,0)=1 → max_depth=1;
  2. 查 mp 有没有深度 1 的记录?没有 → mp [1]=3 → mp 现在 {0:1, 1:3};
  3. 压入 3 的左、右子节点:
    • 3 的左是 5,右是 6 → 先压左(5),再压右(6)→ nodes 栈变成 [2,5,6](栈顶是 6);
  4. 同步压入深度 1+1=2 → depth 栈变成 [1,2,2](栈顶是 2)。

这一步结束后:

  • nodes 栈:[2,5,6]
  • depth 栈:[1,2,2]
  • mp{0:1, 1:3}
  • max_depth:1
第四步:继续循环(处理栈顶的 6)

弹出 6 和深度 2:

  • node=6depth1=2(非 NULL);
  1. 更新 max_depth:max (2,1)=2 → max_depth=2;
  2. 查 mp 有没有深度 2 的记录?没有 → mp [2]=6 → mp 现在 {0:1,1:3,2:6};
  3. 压入 6 的左、右子节点(都是 NULL)→ nodes 栈变成 [2,5,NULL,NULL];
  4. 同步压入深度 3 → depth 栈变成 [1,2,3,3]。

这一步后,mp 已经存好了所有层的最右节点,后续再处理 NULL 节点和剩下的 2、5,都不会修改 mp 了(因为 mp 里对应深度已有值)。

后续循环:处理 NULL 和剩余节点

比如处理 6 的左子节点(NULL):弹出后判断是 NULL,直接跳过,不做任何处理;
处理 5 时,深度是 2,但 mp 里已有深度 2 的记录(6),所以不会覆盖;
处理 2 时,深度是 1,mp 里已有深度 1 的记录(3),也不会覆盖。

直到栈空,循环结束。

最后:生成结果
vector<int> res;
for (int i = 0; i <= max_depth; ++i)  // 从深度0到2,依次取mp的值
{res.push_back(mp[i]);  // 0→1,1→3,2→6 → res=[1,3,6]
}
return res;

关键:为什么先压左子节点,再压右子节点?

这是保证拿到「最右节点」的核心
因为栈是 “先进后出”:先压左,再压右 → 下一次弹出时,会先弹右子节点
比如节点 1 的子节点:先压 2,再压 3 → 下次先弹 3(右节点),这样 3 就会被优先处理,成为深度 1 的最右节点;
节点 3 的子节点:先压 5,再压 6 → 下次先弹 6(右节点),成为深度 2 的最右节点。

如果反过来先压右再压左,就会先处理左节点,拿到的就是「左视图」了

总结:这段代码的逻辑一句话说

用两个栈同步存节点和深度,通过 “先压左、再压右” 保证每次先处理层的右节点,用哈希表记录每一层第一个遇到的右节点(就是最右节点),最后按深度顺序收集结果,就是右视图。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/921254.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/921254.shtml
英文地址,请注明出处:http://en.pswp.cn/news/921254.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kafka面试精讲 Day 7:消息序列化与压缩策略

【Kafka面试精讲 Day 7】消息序列化与压缩策略 在Kafka的高性能消息系统中&#xff0c;消息序列化与压缩是影响吞吐量、延迟和网络开销的核心环节。作为“Kafka面试精讲”系列的第7天&#xff0c;本文聚焦于这一关键主题&#xff0c;深入剖析其原理、实现方式、配置策略及常见…

Xterminal软件下载_Xterminal ssh远程链接工具下载__Xterminal安装包 网盘下载_Xterminal ssh远程链接工具安装包

Xterminal 作为一款国产 SSH 工具&#xff0c;专为开发人员量身打造。它支持 SSH 和 Telnet 协议连接远程服务器与虚拟机&#xff0c;无论是进行代码部署&#xff0c;还是服务器运维&#xff0c;都能轻松胜任。软件界面采用极简设计&#xff0c;黑色背景搭配白色文字&#xff0…

Lua > 洛谷

Lua > 洛谷P1000 超级玛丽游戏P1001 AB ProblemP1008 [NOIP 1998 普及组] 三连击P1035 [NOIP 2002 普及组] 级数求和P1046 [NOIP 2005 普及组] 陶陶摘苹果P1047 [NOIP 2005 普及组] 校门外的树P1085 [NOIP 2004 普及组] 不高兴的津津P1089 [NOIP 2004 提高组] 津津的储蓄计划…

小企业环境-火山方舟和扣子

背景说明 并不是说应该怎么办&#xff0c;而是基本配置有这些可以进行使用&#xff0c;具体不同企业使用的时候肯定要个性化配置。 使用了火山方舟和扣子 火山方舟 应用实验室列表 简单使用了提示词的功能&#xff0c;后端服务ARK_API_KEY 应用ID 来对应请求发送http请求…

QT-事件

Qt事件 除了信号和槽通信机制外&#xff0c;Qt中还提供了事件处理机制实现与用户的交互和对象间的通信。Qt捕获底层操作系统消息&#xff0c;进行封装之后转换为Qt事件&#xff0c;事件处理后才发出信号。 一、事件概述Qt中事件是程序内部或外部发生的动作。比如程序外部&#…

HI3519DRFCV500/HI3519DV500海思核心板IPC算力2.5T图像ISP超高清智能视觉应用提供SDK软件开发包

Hi3519DV500是一颗面向视觉行业推出的超高清智能 SoC。最高支持四路sensor输入&#xff0c;支持最高4K30fps的ISP图像处理能力&#xff0c;支持 2F WDR、多级降噪、六轴防抖、全景拼接、多光 谱融合等多种传统图像增强和处理算法&#xff0c;支持通过AI算法对输入图像进行实时降…

go 初始化组件最佳实践

Go 语言初始化最佳实践 在 Go 语言中, 有一个 init() 函数可以对程序进行包级别的初始化, 但 init() 函数有诸多不便, 例如: 无法返回错误, 进行耗时初始化时, 会增加程序启动时间。因此 init() 函数并不适用于所有初始化。 1.初始化方式 在程序进行初始化时&#xff0c;我们应…

域名暂停解析是怎么回事

域名注册和使用是需要付费的&#xff0c;如果没有及时续费&#xff0c;域名注册商就会暂停该域名的解析服务。相关数据显示&#xff0c;大约有 30% 的域名暂停解析情况是由于欠费引起的。比如&#xff0c;有个小公司的网站域名到期了&#xff0c;负责续费的员工忘记操作&#x…

前端开发的“三剑客”—— ​​HTML、CSS、JavaScript​​

前端开发的“三剑客”—— ​​HTML、CSS、JavaScript​​&#xff0c;是构建所有网页和Web应用的基石。它们分工明确又紧密协作&#xff0c;共同实现了网页的“内容结构”“视觉表现”和“交互行为”。以下是三者的详细解析及协作逻辑&#xff1a;​​1. HTML&#xff1a;网页…

TDengine TIMEDIFF() 函数用户使用手册

TDengine TIMEDIFF() 函数详细使用手册 目录 功能概述函数语法参数说明返回值说明版本变更说明技术特性使用场景及示例时间单位处理数据类型兼容性注意事项常见问题最佳实践 功能概述 TIMEDIFF() 函数用于计算两个时间戳的差值&#xff0c;返回 expr1 - expr2 的结果。结果…

数据结构:栈和队列(上)

汇总代码见&#xff1a;登录 - Gitee.com 上一篇文章&#xff1a;数据结构&#xff1a;双向链表-CSDN博客 与本文相关的结构体传参&#xff1a;自定义类型&#xff1a;结构体-CSDN博客 1.栈 1.1概念和结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端…

文档抽取技术:提取非结构化文档中的关键信息,提升档案管理、金融保险和法律合规领域的效率与准确性

在信息爆炸的时代&#xff0c;各种机构、企业等都面临着海量非结构化文档数据的挑战。报告、合同、票据、档案记录、法律文书等文档中蕴藏着巨大的数据&#xff0c;但传统依靠人工阅读、理解和录入的方式效率低下、成本高昂且容易出错。文档抽取技术作为人工智能和自然语言处理…

雷柏VT1 MAX评测:原生中小手形电竞鼠标 但既不仅限于中小手形 也不仅限于电竞

一、前言&#xff1a;真正针对中小手形设计的电竞鼠标 雷柏第二代VT系列电竞鼠标我们已经体验过很多款了&#xff0c;基本都是针对大中手形设计的外形模具&#xff0c;只有VT3s系列是VT3系列的缩小版&#xff0c;更适合中小手形使用&#xff0c;但也只是对中大手形模具重新优化…

新客户 | TDengine 时序数据库赋能开源鸿蒙物联展区实时监控与展示

在工业物联网快速发展的当下&#xff0c;企业普遍面临着两大挑战&#xff1a;一是设备种类繁多、接入标准不一&#xff0c;导致系统建设容易陷入“数据孤岛”&#xff1b;二是实时监控和多场景联动的需求越来越强烈&#xff0c;但传统数据库在高频写入与多维分析上难以兼顾&…

深入剖析 ConcurrentHashMap:Java 并发编程的基石

目录 【1】Java 7 中 ConcurrentHashMap 的实现原理 1.分段锁&#xff08;Segment&#xff09; 2. 数据结构 3. 操作流程 【2】Java 8 中 ConcurrentHashMap 的改进 1.红黑树的引入 2.CAS 操作 3.数据结构的变化 【3】ConcurrentHashMap 的常用方法及使用示例 1.put(…

【会员专享数据】2020-2022年我国乡镇的逐日地表气压数据(Shp/Excel格式)

之前我们分享过2020—2022年中国0.01分辨率逐日地表气压栅格数据&#xff08;可查看之前的文章获悉详情&#xff09;&#xff01;该数据是研究者张凌, 胡英屹等发布在国家冰川冻土沙漠科学数据中心平台上的高分辨地表气压数据。很多小伙伴拿到数据后反馈栅格数据不太方便使用&a…

第二阶段WinForm-12:UI控件库

1_验证码与条形码 1.1_条码基础知识 条码&#xff1a;条码是由一组按一定编码规则排列的条、空符号组成&#xff0c;用以表示一定的字符、数字及符号组成的信息 1.2_一维码 &#xff08;1&#xff09;Code 128 Code 128 是一种密度很高的字母数字代码系统&#xff0c;可对其…

别再误会了!Redis 6.0 的多线程,和你想象的完全不一样

技术解析核心误区&#xff1a;Redis 6.0是完全多线程的吗&#xff1f;No. Redis 6.0引入的多线程&#xff0c;只用于网络I/O的读写和数据的解析。而核心的命令执行&#xff08;比如 GET, SET, HGETALL 等&#xff09;依然是单线程的。Redis的架构演进&#xff0c;就像是把一个复…

23种设计模式——抽象工厂模式(Abstract Factory Pattern)详解

✅作者简介&#xff1a;大家好&#xff0c;我是 Meteors., 向往着更加简洁高效的代码写法与编程方式&#xff0c;持续分享Java技术内容。 &#x1f34e;个人主页&#xff1a;Meteors.的博客 &#x1f49e;当前专栏&#xff1a;设计模式 ✨特色专栏&#xff1a;知识分享 &#x…

本地部署开源数据生成器项目实战指南

本地部署开源数据生成器项目实战指南 前言 在当今大数据和人工智能时代&#xff0c;高质量数据集对于模型训练和算法开发至关重要。然而&#xff0c;获取真实且合规的数据集往往面临隐私、成本和法律等多重挑战。合成数据生成技术为此提供了优雅的解决方案&#xff0c;它能够…