题目:验证二叉树的先序序列化
One way to serialize a binary tree is to use pre-order traversal. When we encounter a non-null node, we record the node's value. If it is a null node, we record using a sentinel value such as #
.
_9_ / \ 3 2 / \ / \ 4 1 # 6 / \ / \ / \ # # # # # #
For example, the above binary tree can be serialized to the string "9,3,4,#,#,1,#,#,2,#,6,#,#"
, where #
represents a null node.
Given a string of comma separated values, verify whether it is a correct preorder traversal serialization of a binary tree. Find an algorithm without reconstructing the tree.
Each comma separated value in the string must be either an integer or a character '#'
representing null
pointer.
You may assume that the input format is always valid, for example it could never contain two consecutive commas such as "1,,3"
.
Example 1:
"9,3,4,#,#,1,#,#,2,#,6,#,#"
Return true
Example 2:
"1,#"
Return false
Example 3:
"9,#,#,1"
Return false
题意:
先序遍历是序列化二叉树的方式之一。当遇到非空节点时,记录节点的值。如果是空节点,我们将其记为#。
例如,题目描述中的样例二叉树可以序列化为字符串 "9,3,4,#,#,1,#,#,2,#,6,#,#",其中#代表空节点。
给定一个逗号分隔值字符串,校验其是否是一个正确的二叉树先序遍历序列化字符串。设计一个不需要重建树的算法。
每一个逗号分隔值字符串中的值或者是整数,或者是字符'#',代表空节点。
你可以假设输入格式总是有效的,例如,不可能出现两个连续的逗号,比如"1,,3"。
思路一:
利用栈实现。观察正确的二叉树序列化结构可以看出,每一个根节点不为空的节点下面含有一个或者两个“#”字符,不能有连续的三个。将字符串元素压入栈,如果当前栈的深度大于等于3,并且最顶端两个元素为'#', '#',而第三个元素不为'#',则将这三个元素弹出栈顶,然后向栈中压入一个'#',重复此过程。最后判断栈中剩余元素是否只有一个'#'。
"9,3,4,#,#,12,#,#,2,#,6,#,#" 运行步骤值:
stack status
char stack
'9': '9'
'3': '3','9'
'4': '4','3','9'
'#': '#','4','3','9'
'#': '#','3','9'
'12': 'n', '#', '3','9'
'#': '#','1', '#', '3','9'
'#': '#','3','9' -> '#','9'
'2': '2', '#','9'
'#': '#', '2', '#','9'
'6': '6', '#', '2','#','9'
'#': '#', '6', '#', '2','#','9'
'#': '#', '2','#','9' -> '#','9' -> '#'
代码:C++版:8ms
class Solution { public: bool isValidSerialization(string preorder) { stack<char> stk; bool isNum = false; preorder.push_back(','); //假尾,用来结束循环 for (auto c : preorder) { if (c == '#') { while (!stk.empty() && stk.top()=='#') { stk.pop(); //弹出'#' if (stk.empty() || stk.top()=='#') return false; //处理连续三个 '#'的情况 stk.pop(); //弹出数字 } stk.push('#');//使用#替换之前的(#+数字),因为满足(数字+#+#)的模式,验证正确 } else if (c == ',') { if (isNum) stk.push('n'); //使用n代替真实数字 isNum = false; } else { isNum = true; } } return stk.size()==1 && stk.top()=='#'; } };
思路二:转载地址:http://www.cnblogs.com/grandyang/p/5174738.html
通过举一些正确的例子,比如"9,3,4,#,#,1,#,#,2,#,6,#,#" 或者"9,3,4,#,#,1,#,#,2,#,6,#,#"等等,可以观察出如下两个规律:
1. 数字的个数总是比#号少一个
2. 最后一个一定是#号
那么我们加入先不考虑最后一个#号,那么此时数字和#号的个数应该相同,如果我们初始化一个为0的计数器,遇到数字,计数器加1,遇到#号,计数器减1,那么到最后计数器应该还是0。下面我们再来看两个返回False的例子,"#,7,6,9,#,#,#"和"7,2,#,2,#,#,#,6,#",那么通过这两个反例我们可以看出,如果根节点为空的话,后面不能再有节点,而且不能有三个连续的#号出现。所以我们再加减计数器的时候,如果遇到#号,且此时计数器已经为0了,再减就成负数了,就直接返回False了,因为正确的序列里,任何一个位置i,在[0, i]范围内的#号数都不大于数字的个数的。当循环完成后,我们检测计数器是否为0的同时还要看看最后一个字符是不是#号。
代码:C++版:12ms
class Solution { public: bool isValidSerialization(string preorder) { if (preorder.empty()) return false; istringstream in(preorder); vector<string> tree; string val; int count = 0; while (getline(in, val, ',')) tree.push_back(val); //将字符串由‘,’转换为字符串数组 for (int i=0; i<tree.size()-1; ++i) { if (tree[i] == "#") { if (count == 0) return false; else --count; } else ++count; } return count != 0 ? false : tree.back() == "#"; } };实现原理相同,代码:java版本:12ms
public class Solution { public boolean isValidSerialization(String preorder) { String[] nodes = preorder.split(","); int diff = 1; for (String node : nodes) { if (--diff < 0) return false; //--diff每次都会执行 if (!node.equals("#")) diff += 2; //相当于是数字时加1 } return diff == 0; } }
思路三:
使用正则表达式实现。主要实现思想与思路一类似,使用(#)替换掉(数字+#+#),递归调用判断即可。
代码:java版:34ms
public class Solution { public boolean isValidSerialization(String preorder) { String s = preorder.replaceAll("\\d+,#,#", "#"); return s.equals("#") || !s.equals(preorder) && isValidSerialization(s); } }