当前位置: 首页 > news >正文

【数据结构与算法】 二叉树做题

洛谷P8681

完全二叉树按层求权值和最大深度问题

完全二叉树就像:

  • 电影院座位:第一排坐满,第二排坐满,第三排从左到右连续坐人,不留空位

  • 书本排版:每一行都排满文字,最后一行可能不满,但文字都是从左开始

不是完全二叉树就像:

  • 电影院座位:第一排坐满,第二排坐满,第三排中间空一个座位才坐人

满二叉树 vs 完全二叉树(对比表格)

特性满二叉树完全二叉树
定义所有层都满,每个节点都有两个孩子(除了叶子)最后一层可以不满,但必须靠左连续
形状完美的三角形可能缺右下角
节点数固定:2^h - 1(h为高度)范围:2^(h-1) 到 2^h - 1
叶子位置都在同一层只在最后两层
严格程度最严格相对宽松

最近在做一道关于完全二叉树的小题,题目本身不复杂,但挺适合用来巩固对二叉树层序结构的理解。这里简单整理一下思路和实现过程,写成一篇小总结。

题目给定一个包含 N 个节点的完全二叉树,并且节点是按照从上到下、从左到右的顺序给出的,也就是典型的“层序存储”。我们需要做的是:把相同深度的节点权值加起来,找出权值和最大的那一层,如果有多层相同,则输出最小的深度。

一开始看到“完全二叉树 + 顺序存储”,其实就应该想到一个关键点:不需要真的去建树。因为数组本身已经隐含了层序结构。

对于完全二叉树来说,每一层的节点数量是有规律的:

  • 第 1 层:1 个节点

  • 第 2 层:2 个节点

  • 第 3 层:4 个节点

  • 第 k 层:2^(k-1) 个节点

也就是说,我们可以直接按照这个规律在数组中“分段”处理。

核心思路其实很简单:

用一个指针 start 表示当前层的起始位置,用 depth 表示当前是第几层。每一层的节点个数是 2^(depth-1),然后从数组中取出这一段求和,更新最大值即可。

代码如下:

#include<iostream> #include<vector> #include<algorithm> using namespace std; vector<int> arr; int n; int main() { cin >> n; arr.resize(n + 5); for (int i = 1; i <= n; i++) cin >> arr[i]; int ans = 0; // 当前最大权值和 int endans; // 对应的深度 int start = 1; // 当前层起始位置 int depth = 1; // 当前深度 while (start <= n) { int nowsum = 1 << (depth - 1); // 当前层节点数 int sum = 0; for (int i = start; i < start + nowsum && i <= n; i++) { sum += arr[i]; } if (sum > ans) { ans = sum; endans = depth; } start += nowsum; depth++; } cout << endans << endl; return 0; }

这里有几个细节值得注意一下。

首先是1 << (depth - 1),这个是用位运算来计算 2 的幂,比用pow更高效,也更常见。

其次是循环里的边界控制:i <= n。因为最后一层不一定是满的,所以要防止越界。

还有一个点是 start 的更新,每处理完一层,就直接跳到下一层的起始位置,这样整体时间复杂度就是 O(N)。

整体来看,这道题的关键不是二叉树本身,而是“如何利用完全二叉树的结构特性,把问题转化为数组分段处理”。一旦想到这一点,实现就会变得很直接。

这类题目在算法题中很常见,尤其是在不需要真的建树的时候,多考虑一下“下标和结构的关系”,往往能让问题简单很多。

洛谷P4715

淘汰赛求亚军问题的一种递归解法

这道题是一个比较典型的模拟淘汰赛过程的题目。给定 (2^n) 个国家,每个国家都有一个能力值,并且两两对决,能力强的胜出,最终决出冠军。题目要求输出的是“亚军”的编号。

一开始看到这种题,很多人可能会想着用循环一轮一轮模拟比赛,其实这样也可以做。但这道题有一个更自然的解法,就是用递归去还原整个比赛过程,本质上就是一棵“比赛树”。

可以这样理解:整个淘汰赛就是一棵二叉树,每一场比赛都是一个节点,左右子树分别代表两个子赛区的结果,而当前节点就是这两个子赛区冠军之间的对决。

因此,我们可以定义一个函数:在区间 ([l, r]) 内,返回这一段选手的“冠军”。

递归的思路非常直接:

如果区间里只有一个人,那么他就是冠军;
否则就把区间分成左右两部分,分别求出左半区冠军和右半区冠军,然后比较两者能力值,返回更强的那一个。

代码实现如下:

#include <iostream> #include <vector> #include <cmath> using namespace std; struct Team { int id; int ability; }; // 返回区间 [l, r] 的冠军 Team find_champion(vector<int> ability, int l, int r) { if (l == r) { return {l, ability[l]}; } int mid = (l + r) / 2; Team left = find_champion(ability, l, mid); Team right = find_champion(ability, mid + 1, r); if (left.ability > right.ability) { return left; } else { return right; } } int main() { int n; cin >> n; int m = 1 << n; vector<int> ability(m + 1); for (int i = 1; i <= m; i++) { cin >> ability[i]; } // 左右半区冠军 Team left_champion = find_champion(ability, 1, m / 2); Team right_champion = find_champion(ability, m / 2 + 1, m); // 亚军就是最终决赛输掉的人 if (left_champion.ability < right_champion.ability) { cout << left_champion.id << endl; } else { cout << right_champion.id << endl; } return 0; }

这里有一个关键点需要理解清楚:为什么最后只需要比较“左右半区冠军”就能得到亚军?

因为淘汰赛的结构决定了,最终的冠军一定是全场最强的那个选手,而亚军一定是在“决赛”中输给冠军的人。也就是说,亚军只可能出现在另一个半区的冠军里,而不可能是更早被淘汰的人。

所以我们不需要关心整个比赛过程中每一轮的失败者,只需要找到左右两边的“最强者”,然后取较小的那个,就是答案。

这道题的核心其实不在递归本身,而是在于把“淘汰赛”抽象成一棵二叉树结构。一旦有这个思路,代码就会变得很清晰。

另外还有一个可以优化的小点:find_champion里的vector<int> ability是值传递,其实可以改成引用传递,避免不必要的拷贝,这在数据量更大时会更高效。

整体来说,这是一道很适合练习“递归建模”的题,把过程问题转化成结构问题,是算法题里一个很重要的思路。

http://www.cnnetsun.cn/news/1371317.html

相关文章:

  • YOLOv11模型调参指南:如何让交通灯检测准确率提升15%(附训练曲线分析)
  • 知识图谱在教育领域的5个创新应用:从个性化推荐到自适应学习(含Django实现案例)
  • 3.5%稳增!全球电子引信2032年锚定12.41亿美元
  • 2026 年 8 款安卓数据擦除软件和应用对比
  • ESP32玩转SSD1306 OLED:Adafruit_GFX与u8g2库实战对比(附避坑指南)
  • 3大向量索引终极指南:如何在Milvus中选择最适合你的AI应用方案
  • 如何为Steam打造专属交互体验:SFP工具的深度探索
  • TDengine连接池配置实战:HikariCP与Java应用的高效集成指南
  • 三边封制袋机程序(采用松下PLC及威纶通触摸屏控制,前后双伺服送料,高效温控模块常州汇邦
  • 告别重复劳动:用快马AI自动化你的Python数据分析周报任务
  • Mirage Flow 科学计算应用:与MATLAB协同进行数据分析
  • 【Frida Android】实战篇:Java层Hook进阶——拦截与篡改普通方法参数
  • 基于快马平台提升java八股文复习与知识整理效率
  • 回归商业本质,全面升级“三横一纵”出海战略!
  • springboot项目对接质检管理系统
  • Minio+Nginx+Https访问:从零搭建安全文件存储服务
  • AMD Ryzen SDT调试工具:如何精准掌控CPU性能与能效平衡?
  • 个人创作者首选!主流知识付费平台真实体验测评
  • Tesla HW4.0拆解:从5MP摄像头到自研4D雷达,硬件升级全解析
  • RMBG-2.0与爬虫技术结合:自动化采集处理网络图片
  • 构建“T型”AI能力:横向广度与纵向深度的动态平衡,抵御技术迭代风险
  • 脉冲神经网络(SNN)的演进:从基础特性到前沿突破
  • STM32F4 DAC信号发生器实战:如何用DMA+TIM6生成高精度正弦波(附完整代码)
  • COMSOL 远场偏振通用计算方法探索:从理论到实践
  • DeepChat终极指南:如何在5分钟内打造你的AI智能助手工作站
  • 低代码开发如何颠覆传统流程?从概念到落地的全维度指南
  • break,continue,return和exit的区别(详解)
  • 人工智能案例运行为什么会出现卡死的状态?
  • 【第三周】关键词解释:LangChain vs LlamaIndex它们的区别在哪?
  • 主流数据库压测工具实战指南:从选型到性能优化全流程