Medium
题目描述
序列化是将数据结构或对象转换为位序列的过程,以便它可以存储在文件或内存缓冲区中,或通过网络连接链路传输,以便稍后在相同或另一个计算机环境中重新构建。
设计一个算法来序列化和反序列化二叉搜索树。对序列化/反序列化算法应该如何工作没有限制。你需要确保二叉搜索树可以序列化为字符串,并且可以将此字符串反序列化为原始树结构。
编码的字符串应该尽可能紧凑。
示例 1:
输入: root = [2,1,3]
输出: [2,1,3]
示例 2:
输入: root = []
输出: []
约束条件:
- 树中节点数的范围是
[0, 10^4] 0 <= Node.val <= 10^4- 输入的树保证是二叉搜索树
解题思路
本题要求序列化和反序列化二叉搜索树,关键是利用BST的性质来优化存储。
核心思路: 由于BST具有中序遍历有序的特性,我们可以只存储前序遍历的结果,在反序列化时利用BST性质重建树结构。
方法一:前序遍历 + 递归重建(推荐)
- 序列化:使用前序遍历将节点值存储为逗号分隔的字符串
- 反序列化:根据前序遍历结果和BST性质递归重建。对于当前节点,所有小于当前值的节点在左子树,大于当前值的节点在右子树
方法二:前序遍历 + 栈重建 使用栈来模拟递归过程,维护当前节点的取值范围来判断左右子树的归属。
优化点: 相比通用的二叉树序列化(需要存储null标记),BST只需存储节点值,大大减少了存储空间,实现了题目要求的"尽可能紧凑"。
时间复杂度:序列化和反序列化都是O(n),空间复杂度O(n)。
代码实现
class Codec {
public:
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
string result;
preorder(root, result);
return result;
}
private:
void preorder(TreeNode* root, string& result) {
if (!root) return;
result += to_string(root->val) + ",";
preorder(root->left, result);
preorder(root->right, result);
}
public:
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
if (data.empty()) return nullptr;
vector<int> vals;
stringstream ss(data);
string val;
while (getline(ss, val, ',')) {
if (!val.empty()) {
vals.push_back(stoi(val));
}
}
int idx = 0;
return build(vals, idx, INT_MIN, INT_MAX);
}
private:
TreeNode* build(vector<int>& vals, int& idx, int minVal, int maxVal) {
if (idx >= vals.size() || vals[idx] < minVal || vals[idx] > maxVal) {
return nullptr;
}
int val = vals[idx++];
TreeNode* root = new TreeNode(val);
root->left = build(vals, idx, minVal, val);
root->right = build(vals, idx, val, maxVal);
return root;
}
};
class Codec:
def serialize(self, root: Optional[TreeNode]) -> str:
"""Encodes a tree to a single string."""
vals = []
def preorder(node):
if node:
vals.append(str(node.val))
preorder(node.left)
preorder(node.right)
preorder(root)
return ','.join(vals)
def deserialize(self, data: str) -> Optional[TreeNode]:
"""Decodes your encoded data to tree."""
if not data:
return None
vals = list(map(int, data.split(',')))
self.idx = 0
def build(min_val, max_val):
if self.idx >= len(vals) or vals[self.idx] < min_val or vals[self.idx] > max_val:
return None
val = vals[self.idx]
self.idx += 1
root = TreeNode(val)
root.left = build(min_val, val)
root.right = build(val, max_val)
return root
return build(float('-inf'), float('inf'))
public class Codec {
// Encodes a tree to a single string.
public string serialize(TreeNode root) {
List<string> vals = new List<string>();
Preorder(root, vals);
return string.Join(",", vals);
}
private void Preorder(TreeNode root, List<string> vals) {
if (root == null) return;
vals.Add(root.val.ToString());
Preorder(root.left, vals);
Preorder(root.right, vals);
}
// Decodes your encoded data to tree.
public TreeNode deserialize(string data) {
if (string.IsNullOrEmpty(data)) return null;
string[] parts = data.Split(',');
List<int> vals = new List<int>();
foreach (string part in parts) {
if (!string.IsNullOrEmpty(part)) {
vals.Add(int.Parse(part));
}
}
int idx = 0;
return Build(vals, ref idx, int.MinValue, int.MaxValue);
}
private TreeNode Build(List<int> vals, ref int idx, int minVal, int maxVal) {
if (idx >= vals.Count || vals[idx] < minVal || vals[idx] > maxVal) {
return null;
}
int val = vals[idx++];
TreeNode root = new TreeNode(val);
root.left = Build(vals, ref idx, minVal, val);
root.right = Build(vals, ref idx, val, maxVal);
return root;
}
}
var serialize = function(root) {
const vals = [];
function preorder(node) {
if (node) {
vals.push(node.val);
preorder(node.left);
preorder(node.right);
}
}
preorder(root);
return vals.join(',');
};
var deserialize = function(data) {
if (!data) return null;
const vals = data.split(',').map(Number);
let idx = 0;
function build(minVal, maxVal) {
if (idx >= vals.length || vals[idx] < minVal || vals[idx] > maxVal) {
return null;
}
const val = vals[idx++];
const root = new TreeNode(val);
root.left = build(minVal, val);
root.right = build(val, maxVal);
return root;
}
return build(-Infinity, Infinity);
};
复杂度分析
| 操作 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 序列化 | O(n) | O(n) |
| 反序列化 | O(n) | O(n) |
其中 n 为二叉搜索树的节点数量。序列化需要遍历所有节点,反序列化需要重建所有节点,空间复杂度主要来自存储序列化字符串和递归调用栈。