题目来源:LeetCode331:验证二叉树的前序序列化
问题抽象: 验证字符串是否表示有效的二叉树前序序列化(空节点用 "#" 表示),需满足以下核心需求:
-
序列化规则:
- 输入为逗号分隔字符串(如
"9,3,4,#,#,1,#,#,2,#,6,#,#"); - 非
"#"表示真实节点,"#"表示空节点; - 序列化结果需严格匹配二叉树前序遍历结构(根-左-右)。
- 输入为逗号分隔字符串(如
-
有效性判定:
- 槽位计数法:
- 初始化槽位
slots=1(根节点占位); - 遍历每个节点:
- 消耗
1槽位(slots--); - 若当前非空节点,则增加
2槽位(允许扩展左右子节点);
- 消耗
- 若遍历中
slots<0或结束slots≠0,则无效。
- 初始化槽位
- 槽位计数法:
-
边界与约束:
- 空树:输入
"#"有效(slots变化:1→0); - 单节点:输入
"1,#,#"有效(slots:1→0(+2)→1→0); - 无效场景:
- 提前耗尽槽位(如
"1,#":slots:1→0(+2)→1,剩余槽位未归零); - 槽位不足(如
"1,2,#":处理2时slots=-1)。
- 提前耗尽槽位(如
- 空树:输入
-
复杂度要求:
- 时间复杂度 O(n)(单次遍历节点列表,
n为节点数); - 空间复杂度 O(1)(仅需整数
slots计数)。
- 时间复杂度 O(n)(单次遍历节点列表,
输入:字符串 preorder(节点数 ≥1,字符长度 ≤10^4)
输出:布尔值 true/false(表示序列化是否有效)
解题思路
利用前序遍历的特性,通过槽位计数法验证序列的有效性:
- 初始化槽位:根节点需要一个槽位,初始槽位数为
1。 - 遍历节点:
- 遇到非空节点:消耗一个槽位,并增加两个槽位(因为非空节点有两个子节点)。
- 遇到空节点:消耗一个槽位(不增加新槽位)。
- 终止条件:
- 若遍历过程中槽位数为
0但仍有节点未处理,序列无效。 - 遍历结束后槽位数必须恰好为
0,否则序列无效。
- 若遍历过程中槽位数为
关键点:
- 槽位数始终表示剩余可放置节点的位置数量。
- 非空节点净增槽位(
-1 + 2 = +1),空节点净减槽位(-1)。 - 时间复杂度:
O(n),空间复杂度:O(n)(分割字符串的开销)。
代码实现(Java版)🔥点击下载源码
class Solution {
public boolean isValidSerialization(String preorder) {
// 分割字符串为节点数组
String[] nodes = preorder.split(",");
// 初始化槽位:根节点需要1个槽位
int slots = 1;
for (String node : nodes) {
// 若槽位已用完但还有节点未处理,序列无效
if (slots == 0) {
return false;
}
// 处理当前节点
if ("#".equals(node)) {
// 空节点消耗1个槽位
slots--;
} else {
// 非空节点:消耗1个槽位,并新增2个槽位
slots--;
slots += 2;
}
}
// 最终槽位必须恰好为0
return slots == 0;
}
}
代码说明
- 分割字符串:
使用split(",")将输入字符串分割成节点数组nodes。 - 槽位初始化:
slots = 1表示根节点所需的初始槽位。 - 遍历节点:
- 若当前槽位为
0但仍有节点未处理,直接返回false(序列无效)。 - 遇到
#时,槽位减1(消耗一个位置)。 - 遇到非空节点时,槽位先减
1(消耗当前节点位置),再加2(为子节点预留位置)。
- 若当前槽位为
- 最终校验:
遍历结束后检查槽位是否为0,若为0则序列有效,否则无效。
亮点:
- 直接遍历字符串避免分割(需手动解析逗号),但本题输入长度有限,分割法简洁高效。
- 槽位计数法以最小开销完成验证,无额外数据结构操作。
提交详情(执行用时、内存消耗)

541

被折叠的 条评论
为什么被折叠?



