题目:验证二叉树的前序序列化

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

     _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)第331题

链接:https://leetcode-cn.com/problems/verify-preorder-serialization-of-a-binary-tree

分析:

  • 就是每个爸爸都要找到两个儿子。(两个儿子可以是数字也可以是#)
  • #爸爸什么都没有,比较惨。
  • 上面两条任意一条不对都不合法。

思路:

  • 前序遍历严格遵守中,左,右的顺序。
  • 所以第一个为根节点,到第一个#为止,前面这些数字都是根节点的最左边的左子节点
  • 维护一个栈stack,栈中存的是未确认它是否有两个子节点的节点,就是说如果该节点找到了他的两个子节点,就出栈(#也算它的子节点)。
  • 凡是找到数字,全都入栈,因为一开始找到数字,你只能确认它的左子节点是下一个值(数字或#),无法知道它的右子节点。
  • 当找到第一个#时,开始遍历右子节点。因为没有左子节点了,遍历最近的节点的右子节点,也就是栈顶元素!!!
  • 一旦遍历了右子节点,那么该节点的左右子节点都找到了,出栈。
  • 接下来继续重复之前的规律,如果字符串合法,那么stack中的元素都被弹出去了,如果栈中还有元素,那么就说明有的元素找不到它的两个儿子。
  • 大概是这么个思路,不过还有些细节要处理。

看我写的代码:

class Solution:
    def isValidSerialization(self, preorder: str) -> bool:
        if preorder == '':
            return False
        if preorder == '#':
            return True
        pol = preorder.split(',')  # PreOrderList
        stack = []  # 只有数字入栈,当确认了该数字的两个子节点后出栈
        n = len(pol)
        isLeftTree = True  # 如果现在遍历的是左子树的话,不用出栈,如果为右子树的话无论是什么都要出栈
        for i in range(n):
            if pol[i].isdigit():
                if not isLeftTree:
                    if not stack:  # 如果栈是空的还要弹元素的话说明这字符串不合法。
                        return False
                    stack.pop()
                stack.append(pol[i])  # 无论是左子树还是右子树,遇到数字都要入栈
                isLeftTree = True  # 如果遍历到的是数字的话,那么又开始遍历左子树了。
            else:
                isLeftTree = False  # 当左子树遍历到#号时那么左子树完了,开始遍历右子树。
                if pol[i - 1] == '#':
                    if not stack:
                        return False
                    stack.pop()
        return len(stack) == 0  # 栈为空就说明合法,每一个节点都找到了它的两个子节点了。

复杂度分析:

  • 时间复杂度:O(n) n为pol列表长度
  • 空间复杂度:O(n)

总结:

写的不是最优,总的来说就是将数据分为遍历左子树和右子树两种情况。