如何正确统计二叉搜索树中各类节点数量(零子节点、单子节点、双子节点)
本文详解 BST 中 node_counts 方法的常见递归错误,指出忽略子树返回值导致计数失效的问题,并提供两种健壮实现:修正版递归累加与更简洁的闭包+列表计数方案,附可运行示例验证结果为 (4, 0, 3)。
本文详解 bst 中 `node_counts` 方法的常见递归错误,指出忽略子树返回值导致计数失效的问题,并提供两种健壮实现:修正版递归累加与更简洁的闭包+列表计数方案,附可运行示例验证结果为 (4, 0, 3)。
在实现二叉搜索树(BST)节点类型统计功能时,一个典型陷阱是:递归调用子树后未收集并累加其返回的计数值。原始代码中虽正确判断了当前节点的子节点数量(zero/one/two),却直接丢弃了左右子树递归调用的结果,导致最终仅统计了根节点自身类型,故输出恒为 (0, 0, 1) —— 这正是问题根源。
✅ 正确思路:自底向上聚合计数
每个递归调用需完成两件事:
- 递归处理左右子树,获取其各自统计的 (zero, one, two);
- 将子树结果累加到当前层变量,再根据当前节点结构更新自身类别计数。
以下是修复后的 node_counts_aux 实现:
def node_counts_aux(self, node):
zero = 0
one = 0
two = 0
if node is None:
return zero, one, two
# 递归获取左子树计数并累加
z_left, o_left, t_left = self.node_counts_aux(node._left)
zero += z_left
one += o_left
two += t_left
# 递归获取右子树计数并累加
z_right, o_right, t_right = self.node_counts_aux(node._right)
zero += z_right
one += o_right
two += t_right
# 判断当前节点类型并更新计数
if node._left is not None and node._right is not None:
two += 1
elif node._left is not None or node._right is not None: # 精简写法:只需判断“有且仅有一个非空”
one += 1
else:
zero += 1
return zero, one, two
⚠️ 注意:原代码中 elif (node._left is not None and node._right is None) or ... 可简化为 elif node._left or node._right(利用 Python 对 None 的 falsy 特性),更清晰且不易出错。
✨ 更优实践:使用闭包 + 可变容器(推荐)
避免重复传递三元组,改用内部函数操作共享列表,逻辑更直观、代码更紧凑:
NeuralText
Neural Text是一个使用机器学习自动生成文本的平台
def node_counts(self):
"""
Returns the number of nodes with 0, 1, or 2 children in the BST.
Use: zero, one, two = bst.node_counts()
"""
counts = [0, 0, 0] # [zero, one, two]
def _traverse(node):
if node is None:
return
_traverse(node._left)
_traverse(node._right)
# 统计当前节点
if node._left and node._right:
counts[2] += 1
elif node._left or node._right:
counts[1] += 1
else:
counts[0] += 1
_traverse(self._root)
return tuple(counts)
该方案优势明显:
- 无返回值负担:无需设计多变量递归返回协议;
- 状态集中管理:counts 列表在闭包内被安全修改;
- 遍历顺序无关:前序、中序、后序均可(因统计依赖节点自身结构,而非访问时机);
- 语义清晰:counts[0], counts[1], counts[2] 直观对应三类节点。
▶ 验证示例
对如下 BST:
22 / \ 12 30 / \ / \ 8 20 25 40
- 叶节点(0 子节点):8、20、25、40 → 共 4 个
- 单子节点(1 子节点):无 → 共 0 个
- 双子节点(2 子节点):22、12、30 → 共 3 个
调用 node_counts() 将准确返回 (4, 0, 3)。
? 总结
- ❌ 错误模式:递归调用却不接收/累加返回值 → 计数失效;
- ✅ 正确模式:子树结果必须逐层向上合并;
- ? 最佳实践:优先采用闭包 + 可变容器(如 list)简化状态传递;
- ? 测试建议:除完整树外,务必补充含单子节点的用例(如删去 40 后应得 (3, 1, 2)),确保边界逻辑全覆盖。
如非特殊说明,本站内容均来自于网友自主分享,如有任何问题均请联系我们进行处理!
猜您喜欢
晴空双子特蕾西娅全方位攻略
2026-07-07晴空双子特蕾西娅玩法攻略,特蕾西娅有特殊的出伤机制,特蕾西娅的输出手段都围绕【骑士】【狂热之魂】【异端裁决】,主要伤害类型为【冲击伤害】。本期带来新晋限定神话伙伴「特蕾西娅」的全方位养成攻略。 晴空双子特蕾西娅全方位攻略: 一、特性 征伐 战斗开始时在队伍前方【召唤】5名【骑士】,对接近的敌人施放【异端裁决】,造成117%【冲击伤害】并击退【骑士】,进行五次【异端裁决】后进入力竭状态,并在12秒后...
晴空双子爱丽丝角色养成攻略
2026-07-07晴空双子爱丽丝玩法攻略,爱丽丝通过【雪花】造成伤害时将附加【冰冷刺骨】,在6秒内降低目标65%移动速度并降低10%攻击伤害爱丽丝在场时,敌人每持有一种【冰冷】或【冰封】效果,受到的所有伤害提高20%。 晴空双子爱丽丝角色养成攻略: 一、特性介绍 【寒霜转换】每次发射【寒冰裂片】,使队伍获得1层【锋利】;穿透/反弹/侧斩伤害提高4%,飞行速度提高0.5%每次发射【极寒雪球】,使队伍获得1层【厚重】;...
迷你世界铁轨道节点怎么制作
2026-06-27迷你世界游戏里面有很多的道具武器可以去制作,今天给大家带来了游戏里面的铁轨道节点制作方法,还有小伙伴不清楚铁轨道节点怎么制作,那么下面就是相关的内容. 迷你世界铁轨道节点制作方法 合成需要:精铁锭X2、岩石块X2 用来运输过山车,长度限制15格; 放置轨道节点时,显示引导线; 再放置下一个轨道节点时,生成轨道; 切换道具时,可取消引导线。 本文内容来源于互联网,如有侵权请联系删除。
金铲铲之战s8八四机甲怪兽双子星阵容搭配推荐2026
2026-06-24金铲铲之战84机甲怪兽双子星是典型8人口追四费核心运营体系,84含义即在八级人口发力、双四费构筑物法双子星输出,依托3战斗机甲前排承伤搭配多怪兽羁绊补足控制与坦度,整体攻防均衡,不用硬冲9人口也具备吃鸡实力,这套阵容过渡顺滑,魔盗团可快速积累经济提速成型,上限依靠三星卑尔维斯、三星索尔双四费;短板是前期血量管理失误容易容错不足,惧怕成型精英战士斩杀体系。 金铲铲之战s8八四机甲怪兽双子星阵容搭配推...
《轨道双子星》将于2026年9月3日正式登陆 Nintendo Switch™2
2026-06-11数字版与豪华版内容正式公布,即日起开启预售 正如在今日的任天堂直面会中所公布的,由位于东京的独立游戏开发团队Shapefarm倾力打造、KeplerInteractive 发行的复古动漫风双人合作冒险游戏《轨道双子星》,将于2026年9月3日正式发售,并独占登陆NintendoSwitch™2。 此外,玩家从即日起即可预购该游戏。玩家可以选择购买游戏标准版,或是包含独占特典的高级豪华版。 《轨道双...
Notion AI写项目计划提示词怎么写,才能时间节点清楚
2026-06-04要让NotionAI生成时间节点清晰的项目计划,必须用明确时间锚点替代模糊表述,开头写明起止日如“从【2025年6月10日】开始,必须在【2025年9月25日】前完成”,列出里程碑、阶段截止日、每周检查点及前置时限,每项用短横线+动词开头;强制甘特式表达需指定“⏰YYYY-MM-DD:”格式或提供链式样例,并排除相对表述;为适配Timeline视图,须输出两列纯文本表格(任务名|YYYY-MM-D...
如何修复Python aiohttp请求时出现的ClientPayloadError?
2026-06-04ClientPayloadError表明服务端提前关闭连接,非客户端代码错误;常见于服务端超时、拒绝大响应或代理截断,需通过抓包、检查响应头、服务端日志及添加读取超时和异常处理来定位解决。 ClientPayloadError通常意味着服务端提前关闭了连接 这个错误不是客户端代码写错了,而是aiohttp在读取响应体时发现服务端已经断开了连接。常见于服务端超时、主动拒绝大响应、或代理/网关(如Ng...
如何使用Python监控服务器CPU温度并邮件告警_通过psutil模块读取系统信息
2026-06-04psutil无法直接读取CPU温度,因其依赖系统硬件接口且Linux需lm_sensors支持、Windows/macOS完全不支持;可靠方式是用subprocess调用sensors命令解析输出,并配合邮件告警与健壮性设计。 psutil本身不提供CPU温度读取功能——它无法直接获取温度传感器数据,强行调用psutil.sensors_temperatures()在多数Linux发行版(尤其是无...
如何在Python中验证文件的MD5校验和以确保数据完整性?
2026-06-04必须分块读取,推荐8192或65536字节块大小,以rb模式打开文件,循环调用update(),最后用hexdigest()获取32位小写十六进制字符串;校验时需strip().lower()统一格式并做长度校验。 用hashlib.md5()读取文件并计算校验和 直接调用hashlib.md5()并传入整个文件内容(比如md5.update(f.read()))在大文件上会爆内存。必须分块读取,...
怎样在Python Tkinter中使用线程池处理高并发的GUI数据更新?
2026-06-04不能直接用ThreadPoolExecutor更新Tkinter控件,因为Tkinter的Tcl解释器只允许主线程操作UI;子线程(包括线程池中的工作线程)调用widget方法会引发TclError或卡死;必须通过queue.Queue+root.after()将结果安全传回主线程更新。 为什么不能直接用concurrent.futures.ThreadPoolExecutor更新Tkinter控...
