[路飞]_leetcode刷题_331. 验证二叉树的前序序列化

59 阅读3分钟

「这是我参与2022首次更文挑战的第26天,活动详情查看:2022首次更文挑战

题目

331. 验证二叉树的前序序列化

序列化二叉树的一种方法是使用前序遍历。当我们遇到一个非空节点时,我们可以记录下这个节点的值。如果它是一个空节点,我们可以使用一个标记值记录,例如 #

     _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

解法一

栈。

思路

假设树的每一个节点为一个槽位,那么一个数字节点必然会带来两个槽位,并且消耗掉一个槽位,而“#”节点,只会消耗掉1个槽位,最终如果槽位消耗完了,那么说明这个树结构是ok的。

不是树的情况总结有两种:

  1. 树的节点遍历完之后,槽位不为空,那么说明我们的树缺少节点来填充槽位。
  2. 比那里的途中如果我们需要槽位来装数字或者“#”的时候,槽位已经空了,那么树结构也是有问题的。

那么我们可以很快的想到,用栈数据结构来维护这个树的槽位。

具体的逻辑如下

  1. 处理原始数据为一个数组,用','来split。申明一个栈stack来存储槽位。
  2. 遍历数组,每遇到一个数字,我们就消耗一个槽位,增加两个槽位,那么等于就是弹出栈顶的元素,再压入两个元素,至于元素是什么,无所谓,只是用来占个位就好了。
  3. 每遇到一个“#”,我们就消耗一个槽位,即弹出栈顶的元素。
  4. 在这个过程中,每次进入循环我们都要判断当前stack是否为空,如果为空,则直接return false。

代码如下

/**
 * @param {string} preorder
 * @return {boolean}
 */
var isValidSerialization = function(preorder) {
    // 如果树是空的,直接返回true。
    if(preorder.length == 0){
        return true;
    }
    let arr = preorder.split(',');
    let stack = [];
    // 由于我们树为空的状态已经判断过了,那么这里等于是往栈放入根节点的槽位。
    stack.push(0);
    for(let i=0;i<arr.length;i++){
        if(stack.length == 0){
            return false;
        }
        if(/\d/.test(arr[i])){
            stack.pop();
            stack.push(0);
            stack.push(0);
        } else if(arr[i] == '#'){
            stack.pop();
        }
    }
    return stack.length == 0;
};

复杂度分析

时间复杂度:O(n), 遍历一遍数组即可。

空间复杂度:O(n), 需要维护一个栈来存数据,如果树是一个链表,那栈的长度就为n/2,所以空间复杂度为O(n)。

解法二

计数器。

思路

上一个解法其实我们发现,我们用栈的思想来做,每次入栈的内容具体是什么我们不关心,那这里是否有优化的空间呢?当然有,并且可以给空间复杂度优化到O(1)。

就是因为每次入栈的内容我们不关心,那么我们可以用一个计数器来代替栈,每遇到一个数字,我们就把计数器 加1(因为先减1再加2,等于是加1),每遇到一个“#”,则计数器减1,结果判断逻辑和解法一一样。

代码如下

/**
 * @param {string} preorder
 * @return {boolean}
 */
var isValidSerialization = function(preorder) {
    if(preorder.length == 0){
        return true;
    }
    let arr = preorder.split(',');
    let count = 1;
    for(let i=0;i<arr.length;i++){
        if(count == 0){
            return false;
        }
        if(/\d/.test(arr[i])){
            count++;
        } else if(arr[i] == '#'){
            count--;
        }
    }
    return count == 0;
};

复杂度分析

时间复杂度:O(n)。

空间复杂度:O(1),只需要一个计数器来存储基计数即可。