一、题目描述
请实现两个函数,分别用来序列化和反序列化二叉树。
示例:
你可以将以下二叉树:
1
/ \
2 3
/ \
4 5
序列化为 "[1,2,3,null,null,4,5]"
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/xu-lie-hua-er-cha-shu-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
二、思考
从我们这里看到的题目要求是将一颗二叉树能够序列化,并还能够将其反序列化成二叉树。也就是说我们需要保持 null 值来标识某个节点是否还有子节点(第一点要求),第二点要求:序列化的时候我们使用 BFS,三种遍历方式,前、中、后的选择哪个会更好,前序遍历,因为我们一开始就知道了 root 节点,会方便加速我们的序列化和反序列化,万事开头难嘛。
序列化->
反序列化->可举例如下图:
三、代码实现
利用 BFS 实现的
class TreeNode{ int val; TreeNode left; TreeNode right; TreeNode(int x){ this.val = x; } } // Encodes a tree to a single string. public String serialize(TreeNode root) { //第一步:边界特殊值处理 if (root == null){ return ""; } //第二步:BFS搜索,遍历放到队列中 Queue<TreeNode> queue = new LinkedList<>(); queue.offer(root); //第三步:将BFS结果进行封装,并处理好边界的符号 StringBuilder sb = new StringBuilder(); sb.append("["); //第四步:中序遍历,进行节点值的处理 while(!queue.isEmpty()){ TreeNode node = queue.poll(); if (node != null){ sb.append(node.val).append(","); queue.offer(node.left); queue.offer(node.right); }else{ sb.append("null,"); } } sb.deleteCharAt(sb.length() - 1); sb.append("]"); return sb.toString(); }
// Decodes your encoded data to tree. public TreeNode deserialize(String data) { //第一步如果字符串为空,则返回null if (data == ""){ return null; } //第二步:将字符串构建成数组 String varStr= data.substring(1,data.length()-1); String [] arrStr= varStr.split(","); //第三步:构建root节点,因为我们是前序遍历 TreeNode root = new TreeNode(Integer.valueOf(arrStr[0])); //第四步:队列遍历来BFS获取每个节点应该存放的节点值 Queue<TreeNode> queue = new LinkedList<>(); queue.offer(root); int i = 1; //第五步:将数组的值进行构造节点并为下一轮队列遍历做准备 while(!queue.isEmpty() && i <arrStr.length){ TreeNode node = queue.poll(); if (!arrStr[i].equals("null")){ node.left = new TreeNode(Integer.valueOf(arrStr[i])); queue.offer(node.left); } i++; if (!arrStr[i].equals("null")){ node.right = new TreeNode(Integer.valueOf(arrStr[i])); queue.offer(node.right); } i++; } return root; }
复制代码
复杂度分析:
时间复杂度 O(N): N 为二叉树的节点数,层序遍历需要访问所有节点,最差情况下需要访问 N+1 个 null ,总体复杂度为 O(2N + 1) = O(N) 。
空间复杂度 O(N): 最差情况下,队列 queue 同时存储 (N + 1)/2 节点(或 N+1 个 null ),使用 O(N) ;列表 res 使用 O(N) 。
复杂度分析:
时间复杂度 O(N): N 为二叉树的节点数,按层构建二叉树需要遍历整个节点,其长度最大为 2N+1 。
空间复杂度 O(N): 最差情况下,队列 queue 同时存储 (N + 1)/2}个节点,因此使用 O(N)O(N) 额外空间。
如果这个时候还记得我们遍历二叉树的话,我们应该忘记不了 DFS。那么 DFS 我们用递归是否能够简化下代码呢?
序列化
递归的第一步都是特例的处理,因为这是递归的中止条件:如果根节点为空,返回”null“
序列化的结果为:根节点值 + "," + 左子节点值(进入递归) + "," + 右子节点值(进入递归)
递归就是不断将“根节点”值加到结果中的过程
反序列化
先将字符串转换成队列
接下来就进入了递归
i. 弹出左侧元素,即队列出队
ii. 如果元素为“null”,返回 null
iii. 否则,新建一个值为弹出元素的新节点
iv. 其左子节点为队列的下一个元素,进入递归;右子节点为队列的下下个元素,也进入递归
v. 递归就是不断将子树的根节点连接到父节点的过程
参考链接:https://leetcode-cn.com/problems/xu-lie-hua-er-cha-shu-lcof/solution/jian-zhi-offer-37-xu-lie-hua-er-cha-shu-zgixr/
// Encodes a tree to a single string. public String serialize(TreeNode root) { if(root == null){ return "null"; } StringBuilder sb = new StringBuilder();
//中序遍历-利用递归思路将根-左子树-右子树进行处理 return sb.append(root.val).append(",").append(serialize(root.left)).append(",").append(serialize(root.right)).toString(); }
// Decodes your encoded data to tree. public TreeNode deserialize2(String data) { //构建队列数据 Queue<String> queue = new LinkedList<>(Arrays.asList(data.split(","))); return dfs(queue); }
private TreeNode dfs(Queue<String> queue) { //遍历队列数据进行中序遍历 String val = queue.poll(); if("null".equals(val)){ return null; } TreeNode root = new TreeNode(Integer.parseInt(val)); root.left = dfs(queue); root.right = dfs(queue); return root; }
复制代码
复杂度分析
时间复杂度:O(n)
四、小结
其实虽然本题是 hard 题目,但是实际上本身来说,还是考查了面试者对于 DFS 和 BFS 的灵活应用。
评论