【二叉树】验证二叉树的前序序列化

0x00 题目

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

给定一串以 逗号 分隔的序列
验证它是否是 正确 的二叉树的 前序 序列化
编写一个在 重构树的条件下的可行算法

每个以逗号分隔的字符,是一个整数
或者是一个表示 null 指针的 #
你可以认为输入格式总是有效的
例如它永远不会包含两个连续的逗号,比如 1,,3


0x01 思路

方法一:

先定义一个概念,叫做 槽位

一个 槽位 可以被看作当前二叉树中正在等待 被节点填充 的那些位置

二叉树的建立也伴随着 槽位 数量的变化。每当遇到一个节点时:
a.如果遇到了 节点,则要 消耗 一个槽位
b.如果遇到了 非空 节点,则除了 消耗 一个槽位外,还要再 补充 两个槽位

使用栈来维护槽位的变化
栈中的每个 元素,代表了对应节点处剩余槽位的 数量
而栈顶元素就对应着下一步可用的槽位数量

当遇到 节点时,仅将栈顶元素减 1
当遇到 非空 节点时,将栈顶元素减 1 后,再向栈中压入一个 2

无论何时,如果栈顶元素变为 0,就立刻将栈顶 弹出
遍历结束后,若栈 为空
说明没有待填充的槽位,因此是一个 合法 序列

否则若栈 不为空,则序列不合法
此外,在遍历的过程中,若槽位数量 不足,则序列不合法


方法二:计数

能否将方法一的空间复杂度优化至 O(1) 呢?
回顾方法一的逻辑,如果把栈中元素看成一个 整体
即所有剩余槽位的 数量,也能维护槽位的 变化

因此,只维护一个 计数器,代表栈中所有元素之
其余的操作逻辑均可以保持不变


0x02 解法

语言:Swift

解法一:

func isValidSerialization(_ preorder: String) -> Bool {
    let n = preorder.count
    var i = 0
    var stack: [Int] = [1]
    
    while i < n {
    	// 槽位数量不足
        if stack.isEmpty {
            return false
        }
        
        // 取一个字符
        let sIndex = preorder.startIndex
        var index = preorder.index(sIndex, offsetBy: i)
        let c = preorder[index]
        
        // 判断字符
        if c == "," {
            i += 1
        }else if c == "#" {
        	// 空节点
            let last = stack.last! - 1
            if last == 0 {
                stack.removeLast()
            }else{
                stack[stack.count-1] = last
            }
            i += 1
        }else{
            // 读一个数字
            var s = Character("c")
            while i < n && s != "," {
                index = preorder.index(sIndex, offsetBy: i)
                s = preorder[index]
                i += 1
            }
            
            let last = stack.last! - 1
            if last == 0 {
                stack.removeLast()
            }else{
                stack[stack.count-1] = last
            }
            stack.append(2)
        }
    }
    return stack.isEmpty
}

解法二:

func isValidSerializationV2(_ preorder: String) -> Bool {
    let n = preorder.count
    var i = 0
    var slots = 1
    
    while i < n {
        if slots == 0 {
            return false
        }
        
        let sIndex = preorder.startIndex
        var index = preorder.index(sIndex, offsetBy: i)
        let c = preorder[index]
        
        if c == "," {
            i += 1
        }else if c == "#" {
            slots -= 1
            i += 1
        }else{
            // 读一个数字
            var s = Character("c")
            while i < n && s != "," {
                index = preorder.index(sIndex, offsetBy: i)
                s = preorder[index]
                i += 1
            }
            slots += 1 // slots = slots - 1 + 2
        }
    }
    return slots == 0
}


小笔记应用

请添加图片描述


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豪冷啊

你的鼓励是对我的认可!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值