题目描述
序列化二叉树的一种方法是使用前序遍历。当我们遇到一个非空节点时,我们可以记录下这个节点的值。如果它是一个空节点,我们可以使用一个标记值记录,例如 #。给一个二叉树前序序列化的例子如:
字符串 “9,3,4,#,#,1,#,#,2,#,6,#,#”,其中 # 代表一个空节点。
给定一串以逗号分隔的序列,验证它是否是正确的二叉树的前序序列化。编写一个在不重构树的条件下的可行算法。
每个以逗号分隔的字符或为一个整数或为一个表示 null 指针的 ‘#’ 。
你可以认为输入格式总是有效的,例如它永远不会包含两个连续的逗号,比如 “1,3” 。
示例 1:
输入: “9,3,4,#,#,1,#,#,2,#,6,#,#”
输出: true
示例 2:
输入: “1,#”
输出: false
来源:力扣(LeetCode)
(一)构建槽位的思路
构建槽位
对于这道题目,由于是前序遍历,所以序列中父(根)结点总是在子节点的前面。对于这个特性,我们可以考虑构建一个槽位,这个槽位可以理解为一个坑位,将序列遍历,从头节点开始,槽位个数为一,如果头节点不为空(如果为空则不构成二叉树)则槽位个数加2。(因为每个非空节点会产生两个字节点,所以创建两个槽位),同时因为头节点要占一个槽位,所以槽位个数减1。之后继续遍历子节点,遇到非空子节点则槽位个数加2再减1,遇到空子节点则槽位个数减1,继续遍历。如果在遍历的过程中,槽位个数出现负数,说明该序列不符合二叉树前序遍历序列,因为槽位被用完了。整个序列遍历完后,正确的二叉树前序序列的槽位个数应该为0。基于这种槽位的思想,我们就能判定一个序列是否为二叉树前序遍历序列。
C++代码实现:
class Solution {
public:
bool isValidSerialization(string preorder) {
vector <string> stack; //定义存储将序列中的”,“去除之后的序列
string temp="";
for(int i=0;i<preorder.length();i++){
if(preorder[i]!=','){
temp.push_back(preorder[i]);
}
if(preorder[i]==','){
stack.push_back(temp);
temp="";
}
//preorder最后一个字符处理
if(i==preorder.length()-1){
string s1;
char s[2]={preorder[i],0};
s1=s;
stack.push_back(s1);
}
}
//判断二叉树节点数量
if((stack.size()+1)%2!=0){
return false;
}
int slot=1;
//构建槽位
for (auto ch:stack){
slot--;
if(slot<0){
return false;
}
if(ch!="#"){
slot=slot+2;
}
}
if(slot!=0){
return false;
}
return true;
}
};
(二)在所有子树中空节点的数量比非空节点数量多1的思想
对二叉树前序遍历的观察
还是利用之前的一个二叉树前序遍历序列[9,3,4,#,#,1,#,#,2,#,6,#,#]来举例进行观察。在这个序列中,我们能够发现,对于合理的二叉树,空节点的数量总是比非空节点数量多1。
再由于前序遍历的顺序是根左右。我们可以考虑维护一个栈,遇到"#“判断栈是否为空,若为空则判断当前的”#"是否为序列最后一个字符。如果是,说明该序列是二叉树前序遍历序列。如果不是,则说明该序列不是二叉树前序遍历序列。同时,我们应该还考虑到一种情况是当序列遍历完成后,栈中还有数字的情况。这种情况其实也是不合理的二叉树。
C++代码实现
class Solution {
public:
bool isValidSerialization(string preorder) {
if(preorder.empty()){
return false;
}
stack <bool> s;
for(int i=0;i<preorder.length();i++){
if(preorder[i]=='#'){
//判断该"#"是否为序列最后一个字符
if(s.empty()){
return i==preorder.length()-1;
}
else{
//如果栈不为空,则出栈
s.pop();
i++; //这里是为了跳过","
}
}
else{
while((i < preorder.size() && preorder[i] != ',')){
i++; //这里是为了将字符组成一个整体的字符串
}
s.push(0);
}
}
return false ;
}
};
总结
当用两种方法实现后,作者发现第二种方法虽然巧妙,但是不容易想到。对作者本身而言,第一种方法更好想到也更好理解和记忆一些。 但相应的,时间和空间复杂度也相对较高。