问题
Verify Preorder Serialization of a Binary Tree Medium
One way to serialize a binary tree is to use preorder 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 '#'.
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 preorder, return true if it is a correct preorder traversal serialization of a binary tree.
It is guaranteed that 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".
Note: You are not allowed to reconstruct the tree.
Example 1:
Input: preorder = "9,3,4,#,#,1,#,#,2,#,6,#,#"
Output: true
Example 2:
Input: preorder = "1,#"
Output: false
Example 3:
Input: preorder = "9,#,#,1"
Output: false
Constraints:
1 <= preorder.length <= 10^4preorderconsist of integers in the range[0, 100]and'#'separated by commas','.
解题思路
解题之前,我们先来复习一下什么是二叉树的前序序列化吧。按照下图中虚线所示的顺序,将图中二叉树序列化为F,B,A,D,C,E,G,I,H,就是前序序列化。如果按照本题的设定,用#表示空节点,那么前序序列化的结果就是F,B,A,#,#,D,C,#,#,E,#,#,G,#,I,H,#。
那么要如何验证一个序列是否是合法的二叉树的前序序列呢?参照题中给出的样例可知,首先,父节点不能为#,比如9,#,#,1就是非法的;其次,父节点必须有两个子节点,比如1,#就是非法的。换句话说,判断是否合法的条件就是:
- 所有合法父节点都必须有两个子节点
- 所有子节点必须有合法的父节点
如何用程序来检查上述逻辑呢?我们首先要想办法定位一个节点的父节点位置和子节点位置,才能检查它们是否合法。将#自行脑补到上图中,我们可以发现,如果一个节点是左侧子节点,那么它的父节点就是它在序列中的前一个元素。反过来说,如果序列中一个元素是合法的父节点,即nums[i] != "#"时,那么它后面的一个元素就是它的左侧子节点。
相对应的,如果序列中一个元素的前一个元素不是合法的父节点,即nums[i - 1] == "#"时,那么这个元素就是一个右侧子节点。而右侧子节点的父节点需要到前面遍历过的合法父节点里面寻找。所以,我们需要将遍历过的合法父节点保存下来,用于右侧子节点的父节点查找。
我们知道一个合法父节点只能有两个子节点,而它的左侧子节点已经确定是它后面的一个元素,所以在父节点查找中,它只能被使用一次。而且,在父节点查找中,我们并不关心节点的值,只要它不是#就可以。所以我们可以用一个计数器parentCandidateCnt来统计当前可用的合法父节点数。遍历序列的过程中,遇到合法的父节点则parentCandidateCnt++,遇到右侧子节点,则parentCandidateCnt--。
如果最终parentCandidateCnt == 0,则表示所有合法父节点都找到了两个子节点。这样,判断是否合法的条件一就满足了。右侧子节点在执行parentCandidateCnt--时,如果parentCandidateCnt > 0,则表示父节点找到了,判断是否合法的条件二就满足了。
参考答案
public class Solution {
public boolean isValidSerialization(String preorder) {
String[] nums = preorder.split(",");
int n = nums.length;
int parentCandidateCnt = 0;
for (int i = 0; i < n; i++) {
// in preorder traversal, the left child will be right after it's parent
// which means normally nums[i] is the left child of nums[i - 1]
// but # can't be a parent, so when nums[i - 1] == #, nums[i] is should
// be a right child, and we need find its parent from candidates
if (i > 0 && "#".equals(nums[i - 1])) {
if (parentCandidateCnt > 0) {
parentCandidateCnt--;
} else {
// if no candidate left, then return false
return false;
}
}
// the nodes that is not # can be a parent node
if (!"#".equals(nums[i])) {
parentCandidateCnt++;
}
}
// if all parent candidates have found their children return true
return parentCandidateCnt == 0;
}
}
拓展训练
来挑战一下类似的二叉树问题吧!
[Leetcode][Medium] Count Good Nodes in Binary Tree 统计二叉树中好节点的数目 | Java [LeetCode][Medium] Maximum Product of Splitted Binary Tree 分裂二叉树的最大乘积 | Java
或者到作者的LeetCode专栏中看看,有没有其他感兴趣的问题吧!