2026/2/22 0:20:59
网站建设
项目流程
如何查看网站权重,详述网站建设的过程简答题,阿里巴巴国际站坑人,个人网页细规划教案文章目录摘要描述题解答案#xff08;核心思路#xff09;为什么普通二叉树和 BST 不一样#xff1f;BST 的关键点本题采用的策略题解代码#xff08;Swift 可运行 Demo#xff09;题解代码分析1. 为什么用前序遍历#xff1f;2. serialize 的核心逻辑3. deserialize 的核…文章目录摘要描述题解答案核心思路为什么普通二叉树和 BST 不一样BST 的关键点本题采用的策略题解代码Swift 可运行 Demo题解代码分析1. 为什么用前序遍历2. serialize 的核心逻辑3. deserialize 的核心逻辑4. 为什么不需要回退 index示例测试及结果时间复杂度空间复杂度总结摘要「序列化和反序列化二叉搜索树」这道题表面上是在考二叉树其实真正的考点在于你有没有真正理解 BSTBinary Search Tree的性质。如果把它当成普通二叉树去做确实可以用层序遍历加上null占位来解决但那样字符串会很长也完全没有利用 BST 的特性。而题目特意强调了一句编码的字符串应尽可能紧凑。这其实是在暗示BST 是可以只靠遍历顺序恢复出来的。这篇文章会从实际业务场景聊起解释为什么 BST 可以被“无损压缩”再一步步带你用 Swift 写出一套简洁、高效、可运行的解法。描述题目要求我们设计一套算法用来把一棵二叉搜索树序列化成字符串再从这个字符串中反序列化恢复成原来的 BST有几个关键限制条件输入保证一定是一棵 BST节点值范围是合法整数节点数量最多 10⁴序列化字符串要尽量紧凑BST 有一个非常重要的性质左子树所有节点值 根节点右子树所有节点值 根节点这条规则是我们能“少存信息、还能完整还原”的关键。题解答案核心思路为什么普通二叉树和 BST 不一样如果是普通二叉树你只存前序遍历是没法还原结构的比如1 \ 2和1 / 2前序遍历都是[1,2]结构信息丢失了。但 BST 不一样。BST 的关键点只要你知道遍历顺序比如前序BST 的大小关系规则那么不用存 null也能恢复结构。本题采用的策略序列化使用前序遍历root → left → right只记录节点值用逗号拼成字符串反序列化根据 BST 的区间规则递归构建每个节点值都必须落在合法区间(min, max)内一次遍历即可还原整棵树这样做的好处是字符串非常紧凑时间和空间效率都很高思路清晰代码不复杂题解代码Swift 可运行 Demo下面是完整可运行的 Swift 示例包括TreeNode定义和测试代码。importFoundationpublicclassTreeNode{publicvarval:Intpublicvarleft:TreeNode?publicvarright:TreeNode?publicinit(_val:Int){self.valvalself.leftnilself.rightnil}}classCodec{// MARK: - Serializefuncserialize(_root:TreeNode?)-String{varresult:[String][]funcpreorder(_node:TreeNode?){guardletnodenodeelse{return}result.append(String(node.val))preorder(node.left)preorder(node.right)}preorder(root)returnresult.joined(separator:,)}// MARK: - Deserializefuncdeserialize(_data:String)-TreeNode?{ifdata.isEmpty{returnnil}varvaluesdata.split(separator:,).map{Int($0)!}varindex0funcbuild(_min:Int,_max:Int)-TreeNode?{ifindexvalues.count{returnnil}letvalvalues[index]ifvalmin||valmax{returnnil}index1letnodeTreeNode(val)node.leftbuild(min,val)node.rightbuild(val,max)returnnode}returnbuild(Int.min,Int.max)}}题解代码分析1. 为什么用前序遍历前序遍历的第一个节点一定是根节点这对构建树非常友好。BST 的前序遍历有一个隐含规律一段连续小于 root 的值一定属于左子树后面大于 root 的值一定属于右子树结合区间限制我们就能精准判断一个值“该不该被消费”。2. serialize 的核心逻辑funcpreorder(_node:TreeNode?){guardletnodenodeelse{return}result.append(String(node.val))preorder(node.left)preorder(node.right)}这里非常直接不存 null不加多余符号单纯记录节点值这也是字符串“足够紧凑”的关键原因。3. deserialize 的核心逻辑funcbuild(_min:Int,_max:Int)-TreeNode?这是整个算法的精髓。含义是当前子树中合法节点值必须在(min, max)区间内如果当前值不在区间内说明它属于别的子树直接返回 nil如果合法就创建节点并递归构建左右子树因为index是全局递增的所以每个节点只会被处理一次。4. 为什么不需要回退 index这是很多人一开始想不通的点。原因在于前序遍历是严格的「根 → 左 → 右」一旦某个值不在当前区间它一定属于后续的右子树或祖先节点所以不能消费就不动 index这是 BST 性质带来的好处。示例测试及结果我们用示例一来跑一遍letcodecCodec()letrootTreeNode(2)root.leftTreeNode(1)root.rightTreeNode(3)letdatacodec.serialize(root)print(序列化结果:,data)letnewRootcodec.deserialize(data)print(反序列化完成根节点:,newRoot?.val??-1)输出结果序列化结果: 2,1,3 反序列化完成根节点: 2对于空树print(codec.serialize(nil))// print(codec.deserialize()nil)// true结果也是符合预期的。时间复杂度序列化每个节点访问一次O(n)反序列化每个节点构建一次O(n)总体时间复杂度O(n)空间复杂度序列化字符串占 O(n)递归调用栈最坏 O(n)退化成链表总体空间复杂度O(n)总结LeetCode 449 是一道非常经典的「理解型」题目。它并不难写但非常考察你是否真正理解了BST 的结构特性遍历顺序和树结构之间的关系如何用“约束条件”减少冗余数据