449. 序列化和反序列化二叉搜索树
题目描述
序列化是将数据结构或对象转换为一系列位的过程,以便它可以存储在文件或内存缓冲区中,或通过网络连接链路传输,以便稍后在同一个或另一个计算机环境中重建。
设计一个算法来序列化和反序列化 二叉搜索树 。 对序列化/反序列化算法的工作方式没有限制。 您只需确保二叉搜索树可以序列化为字符串,并且可以将该字符串反序列化为最初的二叉搜索树。
编码的字符串应尽可能紧凑。
示例 1:
输入:root = [2,1,3]
输出:[2,1,3]
示例 2:
输入:root = []
输出:[]
提示:
树中节点数范围是 [0, 104]
0 <= Node.val <= 104
题目数据 保证 输入的树是一棵二叉搜索树。
答案
我的答案
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ public class Codec { // Encodes a tree to a single string. public String serialize(TreeNode root) { List<Integer> list = new ArrayList<>(); Queue<TreeNode> queue = new LinkedList<>(); queue.add(root); while (!queue.isEmpty()){ TreeNode poll = queue.poll(); if (poll==null){ list.add(null); }else { list.add(poll.val); queue.add(poll.left); queue.add(poll.right); } } return list.toString().substring(1,list.toString().length()-1); } // Decodes your encoded data to tree. public TreeNode deserialize(String data) { String[] split = data.split(", "); if ("null".equals(split[0])){ return null; } TreeNode root = new TreeNode(Integer.parseInt(split[0])); Queue<TreeNode> queue = new LinkedList<>(); queue.add(root); for (int i = 1; i < split.length; i+=2) { while (!queue.isEmpty()){ TreeNode poll = queue.poll(); TreeNode left = null; TreeNode right = null; if (!"null".equals(split[i])){ left = new TreeNode(Integer.parseInt(split[i++])); queue.add(left); }else { i++; } if (!"null".equals(split[i])){ right = new TreeNode(Integer.parseInt(split[i++])); queue.add(right); }else { i++; } poll.left = left; poll.right = right; } } return root; } }
官方答案
前言
二叉搜索树是一种特殊的二叉树,序列化和反序列化过程也可以参照「297. 二叉树的序列化与反序列化」的过程。二叉搜索树的特殊之处在于其中序遍历是有序的,可以利用这一点来优化时间和空间复杂度。
后序遍历
思路
给定一棵二叉树的「先序遍历」和「中序遍历」可以恢复这颗二叉树。给定一棵二叉树的「后序遍历」和「中序遍历」也可以恢复这颗二叉树。而对于二叉搜索树,给定「先序遍历」或者「后序遍历」,对其经过排序即可得到「中序遍历」。因此,仅对二叉搜索树做「先序遍历」或者「后序遍历」,即可达到序列化和反序列化的要求。此题解采用「后序遍历」的方法。
序列化时,只需要对二叉搜索树进行后序遍历,再将数组编码成字符串即可。
反序列化时,需要先将字符串解码成后序遍历的数组。在将后序遍历的数组恢复成二叉搜索树时,不需要先排序得到中序遍历的数组再根据中序和后序遍历的数组来恢复二叉树,而可以根据有序性直接由后序遍历的数组恢复二叉搜索树。后序遍历得到的数组中,根结点的值位于数组末尾,左子树的节点均小于根节点的值,右子树的节点均大于根节点的值,可以根据这些性质设计递归函数恢复二叉搜索树。
代码
public class Codec { public String serialize(TreeNode root) { List<Integer> list = new ArrayList<Integer>(); postOrder(root, list); String str = list.toString(); return str.substring(1, str.length() - 1); } public TreeNode deserialize(String data) { if (data.isEmpty()) { return null; } String[] arr = data.split(", "); Deque<Integer> stack = new ArrayDeque<Integer>(); int length = arr.length; for (int i = 0; i < length; i++) { stack.push(Integer.parseInt(arr[i])); } return construct(Integer.MIN_VALUE, Integer.MAX_VALUE, stack); } private void postOrder(TreeNode root, List<Integer> list) { if (root == null) { return; } postOrder(root.left, list); postOrder(root.right, list); list.add(root.val); } private TreeNode construct(int lower, int upper, Deque<Integer> stack) { if (stack.isEmpty() || stack.peek() < lower || stack.peek() > upper) { return null; } int val = stack.pop(); TreeNode root = new TreeNode(val); root.right = construct(val, upper, stack); root.left = construct(lower, val, stack); return root; } }
复杂度分析
时间复杂度:O(n),其中 n 是树的节点数。serialize 需要 O(n) 时间遍历每个点。deserialize 需要 O(n) 时间恢复每个点。
空间复杂度:O(n),其中 n 是树的节点数。serialize 需要 O(n) 空间用数组保存每个点的值,递归的深度最深也为 O(n)。deserialize 需要 O(n) 空间用数组保存每个点的值,递归的深度最深也为 O(n)。