0x00 题目

序列化二叉树的一种方法是使用 ​​前序遍历​​​ 当遇到一个 ​​非空​​ 节点时,可以记录下这个节点的值
如果它是一个​​空​​ 节点,可以使用一个标记值记录,例如 ​​#​

给定一串以 ​​逗号​​​ 分隔的序列
验证它是否是 ​​​正确​​​ 的二叉树的 ​​前序​​​ 序列化
编写一个在 ​​​不​​ 重构树的条件下的可行算法

每个以逗号分隔的字符,是一个整数
或者是一个表示 ​​​null​​​ 指针的 ​​#​​​ 你可以认为输入格式总是有效的
例如它永远不会包含两个连续的逗号,比如 ​​1,,3​


0x01 思路

方法一:​​栈​

先定义一个概念,叫做 ​​槽位​

一个 ​​槽位​​​ 可以被看作当前二叉树中正在等待 ​​被节点填充​​ 的那些位置

二叉树的建立也伴随着 ​​槽位​​​ 数量的变化。每当遇到一个节点时:
a.如果遇到了 ​​​空​​​ 节点,则要 ​​消耗​​​ 一个槽位
b.如果遇到了 ​​​非空​​​ 节点,则除了 ​​消耗​​​ 一个槽位外,还要再 ​​补充​​ 两个槽位

使用栈来维护槽位的变化
栈中的每个 ​​​元素​​​,代表了对应节点处剩余槽位的 ​​数量​​ 而栈顶元素就对应着下一步可用的槽位数量

当遇到 ​​空​​​ 节点时,仅将栈顶元素减 ​​1​​​ 当遇到 ​​非空​​ 节点时,将栈顶元素减 ​​1​​ 后,再向栈中压入一个 ​​2​

无论何时,如果栈顶元素变为 ​​0​​​,就立刻将栈顶 ​​弹出​​​ 遍历结束后,若栈 ​​为空​​ 说明没有待填充的槽位,因此是一个 ​​合法​​ 序列

否则若栈 ​​不为空​​​,则序列不合法
此外,在遍历的过程中,若槽位数量 ​​​不足​​,则序列不合法


方法二:​​计数​

能否将方法一的空间复杂度优化至 ​​O(1)​​​ 呢?
回顾方法一的逻辑,如果把栈中元素看成一个 ​​​整体​​​ 即所有剩余槽位的 ​​数量​​,也能维护槽位的 ​​变化​

因此,只维护一个 ​​计数器​​​,代表栈中所有元素之 ​​和​​ 其余的操作逻辑均可以保持不变


0x02 解法

语言:​​Swift​

解法一:

func isValidSerialization(_ preorder: String) -> Bool {
let n = preorder.count
var i = 0
var stack: [Int] = [1]

while i < n {
// 槽位数量不足
if stack.isEmpty {
return false
}

// 取一个字符
let sIndex = preorder.startIndex
var index = preorder.index(sIndex, offsetBy: i)
let c = preorder[index]

// 判断字符
if c == "," {
i += 1
}else if c == "#" {
// 空节点
let last = stack.last! - 1
if last == 0 {
stack.removeLast()
}else{
stack[stack.count-1] = last
}
i += 1
}else{
// 读一个数字
var s = Character("c")
while i < n && s != "," {
index = preorder.index(sIndex, offsetBy: i)
s = preorder[index]
i += 1
}

let last = stack.last! - 1
if last == 0 {
stack.removeLast()
}else{
stack[stack.count-1] = last
}
stack.append(2)
}
}
return stack.isEmpty
}

解法二:

func isValidSerializationV2(_ preorder: String) -> Bool {
let n = preorder.count
var i = 0
var slots = 1

while i < n {
if slots == 0 {
return false
}

let sIndex = preorder.startIndex
var index = preorder.index(sIndex, offsetBy: i)
let c = preorder[index]

if c == "," {
i += 1
}else if c == "#" {
slots -= 1
i += 1
}else{
// 读一个数字
var s = Character("c")
while i < n && s != "," {
index = preorder.index(sIndex, offsetBy: i)
s = preorder[index]
i += 1
}
slots += 1 // slots = slots - 1 + 2
}
}
return slots == 0
}