尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
华为校招机考真题 非AI方向 2026-10-14【脉冲后序右侧和】
华为校招实习机试在线OJ刷题系统 在线OJ注册使用教程目录题目思路Code题目题目内容星际探索途中人类撞见一座年代久远的外星通讯塔。坐标靠一串能量脉冲向外发送。科学家确认这些互不相同的正整数强度其实按二叉排序树组织用来加快检索。接收端拿到的是一整段脉冲。它记录的是这棵树的后序遍历。宇宙射线会把这段记录弄乱于是它未必还能还原成合法的二叉排序树。请完成下面这个核心模块- 校验判断这段强度能否当作某棵合法二叉排序树的后序遍历。- 解码若能够还原再求出这座塔「右缘轮廓」上的强度总和。注「右缘轮廓」是从树的右侧望过去每一层最后映入眼帘的那个结点也就是该层最靠右的结点所带的强度。输出能还原时写下右缘轮廓的强度总和不能还原时记成 −1。数据范围序列长度 m ≤ 10000单个强度不超过 10⁹。输入格式一行若干个互不相同的正整数用空格隔开表示整段强度即二叉树的后序遍历序列。输出格式能还原时输出右缘轮廓的强度总和不能还原时输出 −1。样例1输入3 10 8 5输出23样例2输入3 1 2 1 2 3输出-1 6样例说明后序 3 10 8 5 还原出的二叉排序树为根 5右孩子 88 的右孩子 105 的左孩子 3。三层最靠右的结点依次为 5、8、10总和 23。 注微信原文未提供输入输出描述与样例本节输入输出描述取自牛客镜像同系列官方解析原文亦无独立样例三个样例为镜像标注的验证用样例已全部用参考程序验证。思路整体思路把输入序列当作二叉排序树的后序遍历从后往前用单调栈重建整棵树——后序倒看先根、再右子树、再左子树每个数要么成为栈中某个弹出结点的左孩子要么成为栈顶的右孩子挂孩子时检查值是否落在父结点的开区间里任何一步挂不进去就说明序列非法重建成功后再做一遍先右后左的深度优先遍历每一层第一次遇到的结点就是该层最靠右的结点把这些强度加起来输出。1. 初始化最后一个数是根允许范围记成开区间 (0, 10^18)根入栈这个栈维护的是还没向左分叉的右脊。2. 从倒数第二个数往前扫弹出所有比当前值大的栈顶结点最后弹出的那个就是当前值的父结点当前值挂在它左边值必须严格落在父结点下界父结点值的开区间里同时继承父结点的下界、以父结点值为新上界。3. 一个都没弹出时当前值作为栈顶的右孩子值必须严格落在栈顶值栈顶上界的开区间里继承栈顶值为下界、栈顶上界为上界。4. 任何区间检查不通过立即判定非法输出 -1通过则当前下标入栈。5. 重建成功后用显式栈做先右后左遍历弹出结点时若该深度首次被访问则计入总和然后先压左孩子再压右孩子保证右子树整棵先于左子树被访问。6. 边界只有一个数时循环不执行右缘就是它自己序列含重复值或违反 BST 大小关系都会在区间检查处被判非法。7. 精度m 最大 10000、单个强度最大 10^9总和最大约 10^1364 位整数足够但会超过 32 位整数范围。复杂度时间 O(m)——每个下标最多进栈出栈一次轮廓遍历也只经过每个结点一次空间 O(m)——左右孩子数组、区间边界数组和遍历栈。CodeC/* 脉冲后序右侧和后序序列用单调栈重建 BST 并校验再先右后左遍历累加每层最右结点 */ #include bits/stdc.h using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); vectorlong long seq; long long x; /* 整段强度可能在同一行也可能跨行按空白逐个读完全部 */ while (cin x) seq.push_back(x); int m (int)seq.size(); vectorint left(m, -1), right(m, -1); vectorlong long low(m), high(m); int root m - 1; /* 强度都是正数根的允许区间用开区间 (0, 10^18) */ low[root] 0; high[root] (long long)1e18; vectorint st {root}; /* 从倒数第二个数往前扫栈里保持当前还没向左分叉的右脊 */ for (int i m - 2; i 0; i--) { long long val seq[i]; int parent -1; /* 弹出所有比当前值大的栈顶结点最后弹出的就是当前值的父结点 */ while (!st.empty() seq[st.back()] val) { parent st.back(); st.pop_back(); } if (parent ! -1) { /* 挂到刚弹出结点的左边值必须严格落在父下界, 父值里 */ if (!(low[parent] val val seq[parent])) { cout -1 \n; return 0; } low[i] low[parent]; high[i] seq[parent]; left[parent] i; } else { /* 没有更大的祖先可挂就作为栈顶的右孩子 */ int p st.back(); /* 挂右边值必须严格落在栈顶值, 栈顶上界里 */ if (!(seq[p] val val high[p])) { cout -1 \n; return 0; } low[i] seq[p]; high[i] high[p]; right[p] i; } st.push_back(i); } /* 先右后左走一遍某一层第一次遇到的结点就是该层最靠右的结点 用显式栈而不是递归防止树退化成链时递归过深 */ vectorlong long seen(m, -1); long long total 0; vectorpairint, int walk {{root, 0}}; while (!walk.empty()) { int u walk.back().first; int dep walk.back().second; walk.pop_back(); if (seen[dep] -1) { seen[dep] seq[u]; total seq[u]; } /* 先压左孩子再压右孩子弹栈时右子树整棵先于左子树被访问 */ if (left[u] ! -1) walk.push_back({left[u], dep 1}); if (right[u] ! -1) walk.push_back({right[u], dep 1}); } cout total \n; return 0; }Java/* 脉冲后序右侧和后序序列用单调栈重建 BST 并校验再先右后左遍历累加每层最右结点 */ import java.util.*; import java.io.*; public class Solution { public static void main(String[] args) throws IOException { /* 用 StreamTokenizer 按空白逐个读数不关心换行位置 */ StreamTokenizer in new StreamTokenizer(new BufferedInputStream(System.in)); ListLong list new ArrayList(); while (in.nextToken() ! StreamTokenizer.TT_EOF) { list.add((long) in.nval); } int m list.size(); long[] seq new long[m]; for (int i 0; i m; i) seq[i] list.get(i); int[] left new int[m], right new int[m]; long[] low new long[m], high new long[m]; Arrays.fill(left, -1); Arrays.fill(right, -1); int root m - 1; /* 强度都是正数根的允许区间用开区间 (0, 10^18) */ low[root] 0; high[root] (long) 1e18; DequeInteger st new ArrayDeque(); st.push(root); /* 从倒数第二个数往前扫栈里保持当前还没向左分叉的右脊 */ for (int i m - 2; i 0; i--) { long val seq[i]; int parent -1; /* 弹出所有比当前值大的栈顶结点最后弹出的就是当前值的父结点 */ while (!st.isEmpty() seq[st.peek()] val) { parent st.pop(); } if (parent ! -1) { /* 挂到刚弹出结点的左边值必须严格落在父下界, 父值里 */ if (!(low[parent] val val seq[parent])) { System.out.println(-1); return; } low[i] low[parent]; high[i] seq[parent]; left[parent] i; } else { /* 没有更大的祖先可挂就作为栈顶的右孩子 */ int p st.peek(); /* 挂右边值必须严格落在栈顶值, 栈顶上界里 */ if (!(seq[p] val val high[p])) { System.out.println(-1); return; } low[i] seq[p]; high[i] high[p]; right[p] i; } st.push(i); } /* 先右后左走一遍某一层第一次遇到的结点就是该层最靠右的结点 用显式栈而不是递归防止树退化成链时递归过深 */ long[] seen new long[m]; Arrays.fill(seen, -1); long total 0; Dequeint[] walk new ArrayDeque(); walk.push(new int[]{root, 0}); while (!walk.isEmpty()) { int[] top walk.pop(); int u top[0], dep top[1]; if (seen[dep] -1) { seen[dep] seq[u]; total seq[u]; } /* 先压左孩子再压右孩子弹栈时右子树整棵先于左子树被访问 */ if (left[u] ! -1) walk.push(new int[]{left[u], dep 1}); if (right[u] ! -1) walk.push(new int[]{right[u], dep 1}); } System.out.println(total); } }Go/* 脉冲后序右侧和后序序列用单调栈重建 BST 并校验再先右后左遍历累加每层最右结点 */ package main import ( bufio fmt os ) func main() { /* 输入可能跨行用 bufio.Reader 按词读bufio.Scanner 默认缓冲对大输入不够 */ reader : bufio.NewReaderSize(os.Stdin, 1024*1024) var seq []int64 for { var x int64 if _, err : fmt.Fscan(reader, x); err ! nil { break } seq append(seq, x) } m : len(seq) left : make([]int, m) right : make([]int, m) low : make([]int64, m) high : make([]int64, m) for i : 0; i m; i { left[i], right[i] -1, -1 } root : m - 1 /* 强度都是正数根的允许区间用开区间 (0, 10^18) */ low[root] 0 high[root] 1e18 st : []int{root} invalid : false /* 从倒数第二个数往前扫栈里保持当前还没向左分叉的右脊 */ for i : m - 2; i 0 !invalid; i-- { val : seq[i] parent : -1 /* 弹出所有比当前值大的栈顶结点最后弹出的就是当前值的父结点 */ for len(st) 0 seq[st[len(st)-1]] val { parent st[len(st)-1] st st[:len(st)-1] } if parent ! -1 { /* 挂到刚弹出结点的左边值必须严格落在父下界, 父值里 */ if !(low[parent] val val seq[parent]) { invalid true break } low[i] low[parent] high[i] seq[parent] left[parent] i } else { /* 没有更大的祖先可挂就作为栈顶的右孩子 */ p : st[len(st)-1] /* 挂右边值必须严格落在栈顶值, 栈顶上界里 */ if !(seq[p] val val high[p]) { invalid true break } low[i] seq[p] high[i] high[p] right[p] i } st append(st, i) } if invalid { fmt.Println(-1) return } /* 先右后左走一遍某一层第一次遇到的结点就是该层最靠右的结点 用显式栈而不是递归防止树退化成链时递归过深 */ seen : make([]int64, m) for i : range seen { seen[i] -1 } var total int64 type node struct { u, dep int } walk : []node{{root, 0}} for len(walk) 0 { top : walk[len(walk)-1] walk walk[:len(walk)-1] u, dep : top.u, top.dep if seen[dep] -1 { seen[dep] seq[u] total seq[u] } /* 先压左孩子再压右孩子弹栈时右子树整棵先于左子树被访问 */ if left[u] ! -1 { walk append(walk, node{left[u], dep 1}) } if right[u] ! -1 { walk append(walk, node{right[u], dep 1}) } } fmt.Println(total) }C/* 脉冲后序右侧和后序序列用单调栈重建 BST 并校验再先右后左遍历累加每层最右结点 */ #include stdio.h #include stdlib.h int main(void) { /* 动态数组收集全部整数输入可能跨行不能按行读 */ int cap 16, m 0; long long *seq malloc(sizeof(long long) * cap); long long x; while (scanf(%lld, x) 1) { if (m cap) { cap * 2; seq realloc(seq, sizeof(long long) * cap); } seq[m] x; } int *left malloc(sizeof(int) * m); int *right malloc(sizeof(int) * m); long long *low malloc(sizeof(long long) * m); long long *high malloc(sizeof(long long) * m); int *st malloc(sizeof(int) * m); /* 手写栈存还没向左分叉的右脊下标 */ int top 0; int root m - 1; /* 强度都是正数根的允许区间用开区间 (0, 10^18) */ low[root] 0; high[root] (long long)1e18; st[top] root; int invalid 0; /* 从倒数第二个数往前扫 */ for (int i m - 2; i 0 !invalid; i--) { long long val seq[i]; int parent -1; /* 弹出所有比当前值大的栈顶结点最后弹出的就是当前值的父结点 */ while (top 0 seq[st[top - 1]] val) { parent st[--top]; } if (parent ! -1) { /* 挂到刚弹出结点的左边值必须严格落在父下界, 父值里 */ if (!(low[parent] val val seq[parent])) { invalid 1; break; } low[i] low[parent]; high[i] seq[parent]; left[parent] i; } else { /* 没有更大的祖先可挂就作为栈顶的右孩子 */ int p st[top - 1]; /* 挂右边值必须严格落在栈顶值, 栈顶上界里 */ if (!(seq[p] val val high[p])) { invalid 1; break; } low[i] seq[p]; high[i] high[p]; right[p] i; } st[top] i; } if (invalid) { printf(-1\n); return 0; } /* 先右后左走一遍某一层第一次遇到的结点就是该层最靠右的结点 手写栈存 (结点, 深度)先压左孩子再压右孩子右子树先被访问 */ long long *seen malloc(sizeof(long long) * m); for (int i 0; i m; i) seen[i] -1; long long total 0; int *wu malloc(sizeof(int) * m); int *wd malloc(sizeof(int) * m); int wtop 0; wu[wtop] root; wd[wtop] 0; wtop; while (wtop 0) { wtop--; int u wu[wtop], dep wd[wtop]; if (seen[dep] -1) { seen[dep] seq[u]; total seq[u]; } if (left[u] ! -1) { wu[wtop] left[u]; wd[wtop] dep 1; wtop; } if (right[u] ! -1) { wu[wtop] right[u]; wd[wtop] dep 1; wtop; } } printf(%lld\n, total); free(seq); free(left); free(right); free(low); free(high); free(st); free(seen); free(wu); free(wd); return 0; }Pythondef right_edge_sum(seq): # 判断后序遍历序列能否还原成二叉排序树能则返回右缘轮廓强度总和不能返回 -1 m len(seq) # 下标从 0 计后序遍历的最后一个数是根 left [-1] * m right [-1] * m low [0] * m # 每个结点允许取值的开区间下界 high [0] * m # 每个结点允许取值的开区间上界 root m - 1 # 强度都是正数根的允许区间用开区间 (0, 10^18) low[root] 0 high[root] 10 ** 18 stack [root] # 从倒数第二个数往前扫栈里保持当前还没向左分叉的右脊 for i in range(m - 2, -1, -1): val seq[i] parent -1 # 弹出所有比当前值大的栈顶结点最后弹出的就是当前值的父结点 while stack and seq[stack[-1]] val: parent stack.pop() if parent ! -1: # 挂到刚弹出结点的左边值必须严格落在父下界, 父值里 if not (low[parent] val seq[parent]): return -1 low[i] low[parent] high[i] seq[parent] left[parent] i else: # 没有更大的祖先可挂就作为栈顶的右孩子 p stack[-1] # 挂右边值必须严格落在栈顶值, 栈顶上界里 if not (seq[p] val high[p]): return -1 low[i] seq[p] high[i] high[p] right[p] i stack.append(i) # 先右后左走一遍某一层第一次遇到的结点就是该层最靠右的结点 # 用显式栈而不是递归防止树退化成链时递归过深 seen [-1] * m walk [(root, 0)] total 0 while walk: u, dep walk.pop() if seen[dep] -1: seen[dep] seq[u] total seq[u] # 先压左孩子再压右孩子弹栈时右子树整棵先于左子树被访问 if left[u] ! -1: walk.append((left[u], dep 1)) if right[u] ! -1: walk.append((right[u], dep 1)) return total def main(): import sys # 整段强度可能跨多行直接按空白切分读全部 seq list(map(int, sys.stdin.read().split())) # 不能还原时函数已经返回 -1 print(right_edge_sum(seq)) if __name__ __main__: main()
RELATED

相关推荐

LVS+Keepalived+Nginx 高可用 Web 项目

LVS+Keepalived+Nginx 高可用 Web 项目

一、实验主机配置1.主机信息角色IP备注LVS-Master172.25.254.10lb01LVS-Backup172.25.254.11lb02Nginx RS1172.25.254.20web01Nginx RS2172.25.254.21web02Redis Master172.25.254.30redis-masterRedis Slave172.25.254.31redis-slave监控节点172.25.254.40monitorVIP&#xff…

📅 2026/11/4 2:31:46
数据结构:9.Map和Set

数据结构:9.Map和Set

【目标】掌握 Map/Set 及实际实现类 HashMap/TreeMap/HashSet/TreeSet 的使用掌握 HashMap 和 HashSet 背后的数据结构哈希表的原理和简单实现1.二叉搜索树大家只需要直到二叉搜索树是如何即可,至于Map和Set的底层实现——红黑树,大家现在不需要了解&…

📅 2026/11/4 2:31:46
第306篇 MVVM 与 Flow 组合:现代 Android 分层

第306篇 MVVM 与 Flow 组合:现代 Android 分层

承接上一篇《MVI:单向数据流与状态唯一》,那一篇讲了状态管理的范式,这一篇往下走一层,讲数据是怎么从数据层流到界面的。前四篇讲的是「一个页面内部怎么组织」,这一篇开始讲「多个页面、多个模块之间怎么组织数据」。 结论先行:现代 Android 分层的核心不是多了几个模…

📅 2026/11/4 2:31:46
MORE NEWS

更多资讯

📰

MiniMax Token Plan:智能合约与通证经济解析

1. 项目概述:MiniMax Token Plan 是什么?MiniMax Token Plan 是一个近期上线的创新型数字通证计划,其核心设计理念在于通过智能合约实现收益分配的自动化和透明化。这个项目最吸引人的特点是它采用了"惊喜机制"——参与者除了获得基…

📰

FPGA电子琴设计:从消抖到音阶的完整实现与避坑指南

简介:这份资源面向数字电路与FPGA入门进阶学习者,提供一套基于Verilog硬件描述语言实现的电子琴完整工程,可用于理解音符生成、频率合成、振荡器与音效处理等模块的电路设计思路,适合课程设计、毕业设计或自学练手。压缩包共69个文…

📰

NRBO-SVM时序预测模型:原理、实现与优化技巧

1. 项目概述NRBO-SVM时序预测模型是一种融合了自然随机平衡优化算法(Natural Random Balance Optimization, NRBO)与支持向量机(Support Vector Machine, SVM)的混合预测方法。这个创新方案主要针对传统时序预测中存在的参数选择困难、预测精度不稳定等问题,通过智能…

📰

FPGA电子琴设计实战:从能响到能弹的Verilog方案

简介:这份资源面向数字电路与FPGA入门学习者、电子类课程设计学生及Verilog自学者,提供一套基于ALTERA FPGA平台、用Verilog实现的电子琴完整工程,可用于理解音符生成、频率合成、振荡器与音效处理等模块的硬件逻辑组织方式,并借助…

📰

归并排序算法解析与力扣应用实战

1. 归并排序算法精要解析归并排序作为分治思想的经典实现,其核心在于"分解-解决-合并"的三段式处理流程。在力扣(LeetCode)算法题库中,归并排序不仅是独立的题目(如第912题),更是解决…

📰

最小二乘法在时间序列预测中的实战应用

1. 项目概述时间序列预测是数据分析领域一个经典而实用的课题。从股票价格走势到电力负荷预测,从气象数据建模到设备故障预警,时间序列分析技术已经渗透到我们生产生活的方方面面。在众多预测算法中,最小二乘法(Least Squares, LS…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬