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
参考
https://discuss.leetcode.com/topic/35976/7-lines-easy-java-solution
这题一般是用栈来实现,但是参考链接给出了一个非常新颖的想法,就是用出度和入度来解决。
如果把NULL节点当做叶子节点,那么:
- 所有的非null节点都会有一个入度和两个出度(root除外)
- 所有的null节点都会有一个入度和0个出度。
显然当我们构造这棵树的时候,我们可以计算出度和入度的差值,diff = outdegree - indegree
,如果来了一个新的节点,我们需要把diff - 1(因为入度增加了),如果新来的节点不是null节点,diff就应该+2(因为会有两个出度),如果序列是合法的,那么在构造的过程中,diff 是应该始终大于0的,最后结束的时候diff = 0 。
所以java代码如下
public boolean isValidSerialization(String preorder) {
String[] nodes = preorder.split(",");
int diff = 1;
for (String node: nodes) {
if (--diff < 0) return false;
if (!node.equals("#")) diff += 2;
}
return diff == 0;
}
因为java有split函数,而C++没有,所以我们得自己实现一个split函数:
class Solution {
public:
bool isValidSerialization(string preorder) {
if(preorder.empty()) return true;
int diff = 1;
vector<string> pre;
split(preorder,",",pre);
for(auto s : pre){
--diff;
if(diff < 0) return false;
if(s != "#") diff+=2;
}
return diff==0;
}
void split(const string s,const string delim,vector<string>& ret){
int last = 0;
int index = s.find_first_of(delim,last);
while(index != -1){
ret.push_back(s.substr(last,index-last));
last = index + 1 ;
index = s.find_first_of(delim,last);
}
if(last < s.size())
ret.push_back(s.substr(last));
}
};
至于C++为什么不内置split函数,可以查看相关讨论。http://stackoverflow.com/questions/30967131/why-doesnt-stdstring-contain-a-split-method
方法二就是用栈来实现了
At first glance, a leaf node’s pattern should look like number,#,#
, start from the beginning of array, once you see this pattern, convert it to a single "#"
, meaning the node with value number
has already been fully explored(left subtree, right subtree), so we replace it with a "#"
. While iterating the array, we just keep doing this kind of absorbing/merging backward until we reach the end of array. Then we check if the root has been fully explored, which should eventually be a single #
. During absorbing, if this pattern appears #,#,#,
return false. It’s known that it’s a pain in C++ that there is no split function as Java does, but it won’t matter here since split string is not necessary, we just need to know before ,
it’s a number
or #.
"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' -> '#'
//方法二:stack
class Solution {
public:
bool isValidSerialization(string preorder) {
stack<char> stk;
preorder.push_back(',');
bool is_num = false;
for(auto c : preorder){
if(c == '#'){
while(stk.size() && stk.top() == '#'){
stk.pop();
if(stk.empty() || stk.top() == '#') return false;
stk.pop();
}
stk.push('#');
} else if( c == ','){
if(is_num) stk.push('n');
is_num = false;
} else{
is_num = true;
}
}
return stk.size()==1 && stk.top() == '#';
}
};