题目描述
一种把二叉树序列化的方式是使用前序遍历。当我们遇到非空节点时,直接记录它的值;当我们遇到空节点时,记录#
。
_9_
/ \
3 2
/ \ / \
4 1 # 6
/ \ / \ / \
# # # # # #
例如,上述二叉树可以被序列化成"9,3,4,#,#,1,#,#,2,#,6,#,#"
,#
表示空节点。
给定一个用逗号隔开的序列,请判断它是不是一个合法的二叉树前序遍历。请不要将二叉树重建出来。
被逗号隔开的值要么是整数,要么是#
。
给定的序列一定是合法的。例如,不会出现两个连续逗号的情况:1,,3
。
样例1
输入:"9,3,4,#,#,1,#,#,2,#,6,#,#"
输出:true
样例2
输入:"1,#"
输出:false
样例3
输入:"9,#,#,1"
输出:false
算法
(二叉树遍历) $O(n)$
一般来说,只给出前序遍历,并不能唯一确定一棵二叉树。但这道题目中还给出了所有空节点的位置,所以可以唯一确定一棵二叉树。
我们用先根顺序递归遍历整棵树,遍历时用一个指针在给定数组中指向当前节点的值,如果遇到#
,则说明遇到了空节点,直接return
;如果遇到整数,说明遍历到了树中的一个节点,我们先将指针后移,表示先输出根节点,然后依次递归遍历左子树和右子树。
如果递归还没结束但数组已经遍历完,或者递归结束但数组还没遍历完,则说明给定的序列不是一个合法的前序遍历。
时间复杂度分析:递归遍历时只将数组扫描了一遍,所以时间复杂度是 $O(n)$。
C++ 代码
class Solution {
public:
bool ans = true;
bool isValidSerialization(string preorder) {
preorder += ',';
int u = 0;
dfs(preorder, u);
return ans && u == preorder.size();
}
void dfs(string &preorder, int &u)
{
if (u == preorder.size())
{
ans = false;
return;
}
if (preorder[u] == '#')
{
u += 2;
return;
}
while (preorder[u] != ',') u ++ ; u ++ ;
dfs(preorder, u);
dfs(preorder, u);
}
};
其实也可以选择不给字符串中加逗号,只需要检查一下把所有的数字遍历完成就行
为什么
isValidSerialization
里面 需要preorder += ','
提前加个逗号?后续代码里找当前节点时以
','
为结尾标志,就是这句:while (preorder[u] != ',') u ++ ;
所以为了方便在整个字符串最后加上了一个','
。原谅我太菜了,我还是不太懂为什么dfs里面 if (u == preorder.size()) 就要把ans = false;而结果是ans && u == preorder.size() 那不是肯定返回false吗?
if (u == preorder.size())
是在判断是否在不该结束的时候结束了,如果在不该结束的时候结束了,就返回false
,而最后ans && u == preorder.size();
是在判断是否在该结束的时候没结束。谢谢y总这么快速的解答!
y总 u 和 preorder.size() 都相等了,为啥还是 不该结束的时候结束了 ??
如果调用递归函数,就说明不该结束。
先序遍历一定以#终止dfs。dfs结束时也就是数组遍历结束时。
两个铁球同时着地。
这种思路第一次见过。这又是怎么想到的?
比较直观的想法就是直接模拟一遍前序遍历hh
两个dfs()之间的if可以去掉。
嗯,dfs函数开头的判断完全包含这个if判断,可以去掉。
已去掉~