序列化二叉树的一种方法是使用 前序遍历 。当我们遇到一个非空节点时,我们可以记录下这个节点的值。如果它是一个空节点,我们可以使用一个标记值记录,例如
#
。
例如,上面的二叉树可以被序列化为字符串
"9,3,4,#,#,1,#,#,2,#,6,#,#"
,其中#
代表一个空节点。给定一串以逗号分隔的序列,验证它是否是正确的二叉树的前序序列化。编写一个在不重构树的条件下的可行算法。
保证 每个以逗号分隔的字符或为一个整数或为一个表示
null
指针的'#'
。你可以认为输入格式总是有效的
- 例如它永远不会包含两个连续的逗号,比如
"1,,3"
。注意:不允许重建树。
示例 1:
输入: preorder ="9,3,4,#,#,1,#,#,2,#,6,#,#"
输出:true
示例 2:
输入: preorder ="1,#"
输出:false
示例 3:
输入: preorder ="9,#,#,1"
输出:false
/** * @param {string} preorder * @return {boolean} */ var isValidSerialization = function(preorder) { let n = preorder.length; let i = 0; let stack = [1]; while(i < n){ if(!stack.length) return false; if(preorder[i] == ","){ i++; } else if(preorder[i] == "#"){ stack[stack.length - 1]--; if(stack[stack.length - 1] == 0){ stack.pop(); } i++; } else { while(i < n && preorder[i] !== ","){ i++; } stack[stack.length - 1]--; if(stack[stack.length - 1] == 0){ stack.pop(); } // 不同的是,我们找到的是数字,只要是有效数字必然会有两个儿子节点,还需要我们继续往后找 stack.push(2); } } return !stack.length; }; // 画图来讲下这个图 比较形象 // 根据例子一来看,首先我们有个栈, // 首先,栈里面得有个1,代表即将要添加一个元素进来,然后就拿到这个9,代表拿到一个真正的节点,现在不需要这个1了,给他-1 // 变成0;但是压入栈的数字变成0,代表我们把想要的节点压入进来了,现在开始弹栈了 // 但是一个节点下面有两个儿子节点,所以说我们再给他添加两个节点进来,所以给他压入一个2;压入2 代表我们需要添加两个节点,不管这两个节点是否为空; // 接下来,我们拿到了一个节点3,拿到了一个,就把 2-1;此时还有一个节点未添加;此时3也是个有效节点,有效节点证明3下面也有两个儿子; // 所以再压入一个2;接着往下找,找到了一个4,证明 3已经找到了一个儿子节点,就把这个2-1;这个时候4也是一个有效节点,所以再压入一个2,证明我们下面还需要加两个子节点; // 然后是两个#号,#号代表是空节点,空节点下面是没有儿子的;所以说。我们就只添加不消耗;就把栈顶的2给消耗了 // 然后把这两个#号销掉了,往后找到了一个1,那么这个1 是3的另一个儿子,此时3的两个儿子都已经找打了,所以就把此时栈顶的1,给消耗了。 // 但是这个3 被消耗掉了,这个1也是有效数字,1下面还有两个儿子,再把2给压入栈,发现1 后面是有两个#, // #代表只消耗不增加,所以把栈顶的2 给消耗了; // 后面发现了数字2,从数字3到 2 前面的位置,这一段已经找完了,发现数字是数字9的儿子,所以数字9的位置就是0了 // -1 +2; // 所以9的儿子 3和2已经找完了;发现2也是有效数字,下面也有两个儿子,往后找,发现一个#,#号是只消耗不增加, // 所以栈顶2减去1,所以往后找,发现,2有两个儿子,一个是#号,一个是6,所以栈顶的1被新找到的儿子6给消耗掉了;6下面也有两个儿子 ;压入2,往后找是两个#号,继续消耗,变成了0; 此时栈已经空了,证明这个序列化就是正确的;