序列化是将数据结构或对象转换为一系列位的过程,以便它可以存储在文件或内存缓冲区中,或通过网络连接链路传输,以便稍后在同一个或另一个计算机环境中重建。
设计一个算法来序列化和反序列化 二叉搜索树 。 对序列化/反序列化算法的工作方式没有限制。 您只需确保二叉搜索树可以序列化为字符串,并且可以将该字符串反序列化为最初的二叉搜索树。
编码的字符串应尽可能紧凑。
示例 1:
输入: root = [2,1,3]
输出: [2,1,3]
示例 2:
输入: root = []
输出: []
提示:
- 树中节点数范围是
[0, 10^4] 0 <= Node.val <= 10^4- 题目数据 保证 输入的树是一棵二叉搜索树。
思路
我们以下图的搜索二叉树为例说明
广度优先
因为是二叉树,每个根节点最多有两个子节点,序列化时可以按层级遍历二叉树,当一个节点缺少左节点或右节点时,我们可以用一个二叉树不存在的值来填充,序列化后的数组为[8, 4, 10, 2,6, 9, 12, 1, 3, 5, 7, null, null, null, 11]。反序列化时,我们第 i 层节点最多有 2^i 个节点,第 n 个节点,它的左节点是第 2n + 1 个节点,右节点是 2n + 2 个节点。知道了上面的关系,我们就可反序列化了。
前序遍历
因为是搜索二叉树,我们可以用前序遍历或后续遍历进行二叉树的序列化和反序列化,以前序遍历为例,序列化后的数组为[8, 4, 2, 1, 3, 6, 5, 7, 10, 9, 12, 11],反序列化时,我们知道第一个值是跟节点,即8是跟节点,左子树都小于8, 右子树都大于8,我们可以遍历找到第一个大于8的值的位置并分割数组,[4, 2, 1, 3, 6, 5, 7]为左子树,[10, 9, 12, 11]为右子树,再分别对左右子树进行相同的操作,直到没有子树为止。
解题
/**
* Encodes a tree to a single string.
*
* @param {TreeNode} root
* @return {string}
*/
var serialize = function (root) {
const arr = [];
const dfs = (root) => {
if (!root) {
return;
}
arr.push(root.val);
dfs(root.left);
dfs(root.right);
};
dfs(root);
return arr;
};
/**
* Decodes your encoded data to tree.
*
* @param {string} data
* @return {TreeNode}
*/
var deserialize = function (data) {
if (!data?.length) return null;
const bf = (left, right, val) => {
while (left <= right) {
const mid = (left + right) >> 1;
if (data[mid] >= val) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return left;
};
const dfs = (nums, left, right) => {
const val = nums[left];
const node = new TreeNode(val);
if (right > left) {
const idx = bf(left + 1, right, val);
if(idx>left+1){
node.left = dfs(nums, left+1, idx-1)
}
if(idx<right+1){
node.right = dfs(nums, idx, right)
}
}
return node
};
return dfs(data, 0, data.length-1)
};