算法题分享 | 验证二叉树的前序序列化

34 阅读3分钟

像素风 电脑壁纸 宝马 16_9_1_小渔仔~_来自小红书网页版.jpg

题目

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

例如,上面的二叉树可以被序列化为字符串 "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

 

提示:

  • 1 <= preorder.length <= 104
  • preorder 由以逗号 “,” 分隔的 [0,100] 范围内的整数和 “#” 组成

题解

解题思路

方法一

大致思路是我们可以按前序顺序把给出的元素填充到树中,如果刚好可以填充完成,则表示是合法的,否则则非法。

虽然不能重建树,但是我们可以借助栈来判断,设定栈中的每个元素值(整数)表示对应树节点当前剩余的节点槽数(可填充节点数)。

处理过程大致为:

  1. 如果当前元素是数字,则需要把当前栈顶节点值减 1(如果减为 0,则出栈),并且给栈中 push 节点 2,这是因为当前元素填充后会占掉一个槽,但是该元素同时会再提供两个槽(左右子树);
  2. 如果当前元素是 #,因为是空节点,所以只需把当前栈顶节点值减 1(如果减为 0,则出栈),无需增加槽。

在此过程中,如果字符串中元素还没处理完,出现栈为空的情况,说明不是正确的二叉树的前序序列化,直接返回 false 即可。待所有元素处理完,此时如果栈为空,则说明合法。之所以这种方法成立,主要是因为我们每填充一个非空元素,都给剩余节点槽数加 1(+2-1),这符合二叉树的特性,所以如果是正确的二叉树的前序序列化,最终肯定能填充完成并且剩余节点槽数为 0。

方法二

方法一实现的空间复杂度为 O(n),可以考虑优化为常量级的空间复杂度。

方法一的实现中,不难发现,实际其中的栈可以看作是一个整体,实际无需使用栈存储各节点对应的剩余节点槽数,而是可以直接使用一个整数来存储总的剩余槽数即可。

这样一来,空间复杂度就优化为了 O(1),而且实现也比较简单,下面给出的代码正是基于这种方法。

代码

class Solution {
    public boolean isValidSerialization(String preorder) {
        String[] splits = preorder.split(",");
        int slots = 1;

        for (String s : splits) {
            if (slots == 0) {
                return false;
            }    
            if (s.equals("#")) {
                // 空结点
                slots--;
            } else {
                // 数字节点  slots = slots - 1 + 2
                slots++;
            }
        }

        return slots == 0;
    }
}

复杂度分析

  • 时间复杂度:O(n)\

  • 空间复杂度:O(1)

优质项目推荐

推荐一个可用于练手、毕业设计参考、增加简历亮点的项目。

lemon-puls/txing-oj-backend: Txing 在线编程学习平台,集在线做题、编程竞赛、即时通讯、文章创作、视频教程、技术论坛为一体