题目
解法:
1.栈模拟
(1).前序遍历(NLR)
前序遍历就是 首先访问根结点然后遍历左子树,最后遍历右子树。
- 对于字符串s判断其是否为先序遍历的二叉树,可以模拟先序遍历,可以按规则将字符串全部遍历返回true;
- 左结点遍历到底,遍历右结点需要栈来辅助,退栈到这个结点的父结点在遍历右结点。由于这个题是判断,所以没必要入栈具体的结点,只需要计数来确保有父结点可以退就ok。
- ps:计数栈初始值:因为当退回到根节点,退栈到0 但右结点还可以遍历,所以要设置为1;
(2).代码
//初值设置为1
class Solution {
public boolean isValidSerialization(String preorder) {
int stack = 1, i = 0, n = preorder.length() ;
if(n<1) return false;
while(i<n)
{
if(stack==0) return false;
char ch = preorder.charAt(i);
if(ch!='#')
{
stack++;
while(i+1 < n && preorder.charAt(i+1)!=',')
++i; //跳跃数字
i+=2; //省略,
}else
{
stack--;
i+=2;
}
}
return stack==0;
}
}
时间复杂度 :
O
(
n
)
O(n)
O(n) ,一次遍历
空间复杂度 :
O
(
1
)
O(1)
O(1) ,常数辅助空间
// 初值设为0的情况,要特殊处理根结点,并且退栈是是在访问过#空结点后进行。
class Solution {
public boolean isValidSerialization(String preorder) {
int stack = 0, i = 0, n = preorder.length() ;
boolean pop = false;
if(n<1) return false;
if(preorder.charAt(i)!='#') {
while(i+1 < n && Character.isDigit(preorder.charAt(i+1)))
++i;
i+=2;
stack++;
}else
{
if(n==1) return true;
return false;
}
while(i<n && stack!=0)
{
if(pop) { stack--; pop = false; }
char ch = preorder.charAt(i);
if(ch!='#')
{
stack++;
while(i+1 < n && Character.isDigit(preorder.charAt(i+1)))
++i; //跳跃数字
i+=2; //省略,
}else
{
pop = true;
i+=2;
}
}
if(i==(n+1) && stack == 0) return true;
return false;
}
}
时间复杂度 :
O
(
n
)
O(n)
O(n) ,一次遍历
空间复杂度 :
O
(
1
)
O(1)
O(1) ,常数辅助空间
2.入度出度统计
(1) 树的性质:
- 所有顶点的出度之和总是等于入度之和
- 本题的空结点也当做结点,所以(1)这个结点是没有出度的, (2)所有非空结点的出度一定为2
- 故可以将非空结点的入度设为1,出度设为2,空结点的入度设为1,出度设为0. 一次遍历,判断出度之和是否等于入度之和
(2). 代码
class Solution {
public boolean isValidSerialization(String preorder) {
char[] str=preorder.toCharArray();
int out=0,in=-1;
for(int i=0;i<str.length;i++){
if(str[i]==',') continue;
in++;
if(out-in<0) return false; //空结点后接上了非空结点。
if(str[i]<='9'&&str[i]>='0'){
out+=2;
while(i<str.length-1&&str[i+1]!=',') i++; //跳过数字
}
}
return out-in==0;
}
}