「这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战」
前言
今天这道题我们会用到栈和二叉树知识的一个结合体,来验证一个二叉树的字符串表示形式是否对当前这颗二叉树进行了正确的前序序列化。
题目描述
331. 验证二叉树的前序序列化
序列化二叉树的一种方法是使用前序遍历。当我们遇到一个非空节点时,我们可以记录下这个节点的值。如果它是一个空节点,我们可以使用一个标记值记录,例如 #。
例如,上面的二叉树可以被序列化为字符串 "9,3,4,#,#,1,#,#,2,#,6,#,#",其中 # 代表一个空节点。
给定一串以逗号分隔的序列,验证它是否是正确的二叉树的前序序列化。编写一个在不重构树的条件下的可行算法。
每个以逗号分隔的字符或为一个整数或为一个表示 null 指针的 '#' 。
你可以认为输入格式总是有效的,例如它永远不会包含两个连续的逗号,比如 "1,,3" 。
来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/ve… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路
题给输入序列化字符串:
由整数、逗号、'#'组成的有效序列化字符串来表示二叉树的序列化字符串,'#'表示值为null的空节点的占位符,且不会出现连续逗号,形如题中示例二叉树序列字符串"9,3,4,#,#,1,#,#,2,#,6,#,#";
我们的任务:
验证形如"9,3,4,#,#,1,#,#,2,#,6,#,#"的序列化字符串是否是正确的前序序列化结果
怎么样才算正确的前序序列化结果呢?
第一步是从根节点开始遍历决定验证前序的关键
-
前序序列化的遍历顺序是根、左、右,我们
需要从根节点开始验证,那么标准字符串最小长度就是1
,就是根节点是空的情况,此时标准字符串就是一个“#”,那么此时可能你会疑惑
,如果是中序和后序,会有区别吗?中序和后序都是从左子节点开始,证明它肯定是有根节点的,所以标准字符串长度最小是3,即根节点为整数,另外两个是空,为‘#’占位符号; -
如果
根节点是整数,那么表示后面还有至少还有两个节点占位
,标准字符串长度至少为1+2=3; 第二步用什么工具来决定序列化的结果 -
通过栈来管理有效节点个数,遍历过程中按照正确的前序节点个数来添加和删除节点个数,最后为空栈则表示题给字符串验证有效:
1)初始情况[1]表示我们字符串最小长度为1,如果根节点为空,栈顶元素减1,如果栈顶元素为0表示当前栈顶个数代表的有效节点已经判断ok,直接执行出栈操作2)如果遇到整数,说明当前节点是有效节点,那么栈顶元素的有效节点个数减1,同样入栈2作为新的栈顶元素代表当前有效节点还需至少2个占位,占中的数字之和就是后面要验证的有效二叉树的节点占位个数之和: 例如:[1] => [0] => [] => [2]
3)如果遇到'#',说明当前是空节点,空节点也完成了一次有效占位的验证,栈顶元素值减1即可,减1之后如果栈顶元素值为0,直接出栈,表明上个节点已验证有效完毕
开始解题
var isValidSerialization = function(preorder) {
let len = preorder.length, i = 0, stack = [1]; // 初始化有效前序序列字符串节点个数为1
while(i < len) { // 遍历题给二叉树序列字符串
if(!stack.length) return false; // 如果还在遍历过程中,栈空了,说明序列化错误,后面还有节点,前面没有根节点了肯定是错误的
if(preorder[i] === ',') i++; // 遇到逗号直接进行下一次遍历;
else if(preorder[i] === '#') { // 遇到#栈顶待验证有效个数减1,如果栈顶待验证有效个数为0直接栈顶出栈
stack[stack.length-1]--;
if(stack[stack.length-1] === 0) stack.pop();
i++;
}else {
while(i < len && preorder[i] !== ',') { // 这里是为了处理数字可能不是1位的情况
i++;
}
stack[stack.length-1]--; // 是整数就表示栈顶待验证有效个数需减1,
if(stack[stack.length-1] === 0) { // 如果栈顶待验证有效个数为0直接栈顶出栈
stack.pop();
}
stack.push(2); // 是整数就表明后面还至少有2个待验证有效节点占位
}
}
return !stack.length;
};