哈哈哈,孩子高兴疯了,半个小时的时间编出代码,用时打败100%,内存打败87.91%,孩子从来打代码没有这么爽过。。。。。。
提示:这题感觉属于抖机灵那种,没什么编程技巧,代码量也不大
一、题目是什么?
序列化二叉树的一种方法是使用前序遍历。当我们遇到一个非空节点时,我们可以记录下这个节点的值。如果它是一个空节点,我们可以使用一个标记值记录。
_9_
/ \
3 2
/ \ / \
4 1 # 6
/ \ / \ / \
# # # # # #
例如,上面的二叉树可以被序列化为字符串 “9,3,4,#,#,1,#,#,2,#,6,#,#”,其中 # 代表一个空节点。
给定一串以逗号分隔的序列,验证它是否是正确的二叉树的前序序列化。编写一个在不重构树的条件下的可行算法。
每个以逗号分隔的字符或为一个整数或为一个表示 null 指针的 ‘#’ 。
你可以认为输入格式总是有效的,例如它永远不会包含两个连续的逗号,比如 “1,3” 。
示例 1:
输入: “9,3,4,#,#,1,#,#,2,#,6,#,#”
输出: true
示例 2:
输入: “1,#”
输出: false
示例 3:
输入: “9,#,#,1”
输出: false
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/verify-preorder-serialization-of-a-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
二、解答
1.我的解法
我的想法很简单,就是判断字符串里面#的个数是不是数字个数+1.
在之前的离散数学,数据结构里,我记得这样一条性质,二叉树里面,度为0的节点(也就是本题的空节点)是度为2的节点的数目+1,具体证明就是算两次,找边和顶点直接的关系,在此就不赘述了。
然后在反复提交修改的过程中,加了三个细节:
1、数字可以为多位的
2、空树的判断
3、null=node(d=2)+1,更完善一些,一旦先序序列的前 i 个元素满足了上面的关系,此时,如果后面还有元素,这也不是一颗二叉树。
代码如下:
class Solution {
public:
bool isValidSerialization(string preorder) {
int n=preorder.size();
int pre=0;
int tree=0,node=0;
if(preorder[0]=='#'&&n!=1) return false;//空树判断
for(int i=0;i<n;i++){
if(preorder[i]=='#') node++;
if(isdigit(preorder[i])&&pre!=i-1){//注意,这里的数字不一定是个位,要考虑到多位的情况
pre=i;
tree++;}
if(node==tree+1&&i!=n-1) return false; //如果前面已经成树,那么后面就不可能再和前面组成一棵新的二叉树了
}
if(node==tree+1) return true;
else return false;
}
};
2.官方解法
让我小小的骄傲一下,此时我想大声的说一句话:你个垃圾!!!(心酸,这都是以前我看完官方代码之后对自己说的话,风水轮流转啊。。。嚣张ing)官方思路:
方法一:栈
我们可以定义一个概念,叫做槽位。一个槽位可以被看作「当前二叉树中正在等待被节点填充」的那些位置。
二叉树的建立也伴随着槽位数量的变化。
如果遇到了空节点,则要消耗一个槽位;
如果遇到了非空节点,则除了消耗一个槽位外,还要再补充两个槽位。
此外,还需要将根节点作为特殊情况处理。
我们使用栈来维护槽位的变化。栈中的每个元素,代表了对应节点处剩余槽位的数量,而栈顶元素就对应着下一步可用的槽位数量。当遇到空节点时,仅将栈顶元素减 1;当遇到非空节点时,将栈顶元素减 1 后,再向栈中压入一个 2。无论何时,如果栈顶元素变为 0,就立刻将栈顶弹出。
遍历结束后,若栈为空,说明没有待填充的槽位,因此是一个合法序列;否则若栈不为空,则序列不合法。此外,在遍历的过程中,若槽位数量不足,则序列不合法。
复杂度分析
时间复杂度:O(n),其中 n为字符串的长度。我们每个字符只遍历一次,同时每个字符对应的操作都是常数时间的。
空间复杂度:O(n)。此为栈所需要使用的空间。
方法二:计数
能否将方法一的空间复杂度优化至 O(1) 呢?
回顾方法一的逻辑,如果把栈中元素看成一个整体,即所有剩余槽位的数量,也能维护槽位的变化。
因此,我们可以只维护一个计数器,代表栈中所有元素之和,其余的操作逻辑均可以保持不变。
PS:个人感觉代码一直在数空槽位的个数
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
方法二代码如下(示例):
class Solution {
public:
bool isValidSerialization(string preorder) {
int n = preorder.length();
int i = 0;
int slots = 1;
while (i < n) {
if (slots == 0) {
return false;//如果没有槽位了,但是还有元素,那么这个树就是错的啦
}
if (preorder[i] == ',') {
i++;
} else if (preorder[i] == '#'){
slots--;//遇到空节点,槽位被占用
i++;
} else {
// 读一个数字
while (i < n && preorder[i] != ',') {
i++;
}
slots++; // slots = slots - 1 + 2,遇到非空节点,槽位+1
}
}
return slots == 0;//判断条件哦
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/verify-preorder-serialization-of-a-binary-tree/solution/yan-zheng-er-cha-shu-de-qian-xu-xu-lie-h-jghn/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
总结
本题有两种思路,一种就是我的想法,发掘二叉树的数学性质,找到判断二叉树的充要条件。
一种就是官方的,找槽位,他没有深入挖掘二叉树的数学性质,但是想的很直接,只要没有槽位可以让节点填充,那么这个树就是false,然后维护一个计数器。其实这种想法更正统一般一些,有值得学习的地方。