问题描述 链接到标题
331. 验证二叉树的前序序列化 (Medium)
序列化二叉树的一种方法是使用 前序遍历。当我们遇到一个非空节点时,我们可以记录下这个节点的值。如
果它是一个空节点,我们可以使用一个标记值记录,例如 #
。
例如,上面的二叉树可以被序列化为字符串 "9,3,4,#,#,1,#,#,2,#,6,#,#"
,其中 #
代表一个空节点。
给定一串以逗号分隔的序列,验证它是否是正确的二叉树的前序序列化。编写一个在不重构树的条件下的可行算法 。
保证 每个以逗号分隔的字符或为一个整数或为一个表示 null
指针的 '#'
。
你可以认为输入格式总是有效的
- 例如它永远不会包含两个连续的逗号,比如
"1,,3"
。
注意: 不允许重建树。
示例 1:
输入: preorder = "9,3,4,#,#,1,#,#,2,#,6,#,#"
输出: true
示例 2:
输入: preorder = "1,#"
输出: false
示例 3:
输入: preorder = "9,#,#,1"
输出: false
提示:
1 <= preorder.length <= 10⁴
preorder
由以逗号“,”
分隔的[0,100]
范围内的整数和“#”
组成
解题思路 链接到标题
归约 链接到标题
本质上还是一种递归的思想,在前序遍历时,我们可以注意到,每个叶子结点必定跟随两个 null,因此,我们可以反过来,将连续的一个非空结点和两个空结点,归约为一个空结点,这个过程有点像消消乐,可以利用栈来实现这个过程,最后根据栈是否只剩下一个空结点来判断。
递归 链接到标题
递归的思路参照 剑指 Offer 37. 序列化二叉树 (Hard),不同之处在于我们只需要判断左子树和右子树是否满足要求而已,不需要返回它们的头结点。
代码 链接到标题
归约 链接到标题
class Solution {
public:
void string2vec(string &preorder, vector<string> &strs) {
for (int i = 0; i < preorder.size(); ++i) {
int r = i;
while (r < preorder.size() && preorder[r] != ',') {
++r;
}
strs.push_back(preorder.substr(i, r - i));
i = r;
}
}
bool isValidSerialization(string preorder) {
vector<string> strs;
string2vec(preorder, strs);
int n = strs.size();
stack<string> stk;
for (int i = 0; i < n; ++i) {
if (strs[i] != "#") {
stk.push(strs[i]);
} else {
while (stk.size() >= 2 && stk.top() == "#") {
stk.pop();
if (stk.top() == "#") {
return false;
}
stk.pop();
}
stk.push("#");
}
}
return stk.size() == 1 && stk.top() == "#";
}
};
递归 链接到标题
class Solution {
public:
void string2vec(string &preorder, vector<string> &strs) {
for (int i = 0; i < preorder.size(); ++i) {
int r = i;
while (r < preorder.size() && preorder[r] != ',') {
++r;
}
strs.push_back(preorder.substr(i, r - i));
i = r;
}
}
bool dfs(vector<string> &strs, int &idx) {
if (idx >= strs.size()) {
return false;
}
if (strs[idx] == "#") {
++idx;
return true;
}
++idx;
return dfs(strs, idx) && dfs(strs, idx);
}
bool isValidSerialization(string preorder) {
vector<string> strs;
string2vec(preorder, strs);
int idx = 0;
return dfs(strs, idx) && idx == strs.size();
}
};