【面试题】序列化二叉树(DFS+BFS)

题目描述

【面试题】序列化二叉树(DFS+BFS)_第1张图片

解法一:层序遍历(BFS)

/**
 * 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) {
     
        if(root == null) return "[]";
        StringBuilder res = new StringBuilder();
        res.append("["); 
        // 借助队列实现层序遍历
        Queue<TreeNode> q = new LinkedList<> ();
        q.offer(root);
        while(!q.isEmpty()) {
     
            TreeNode node = q.poll();
            if(node != null) {
     
                res.append(node.val + ",");
                q.offer(node.left);
                q.offer(node.right);
            }
            else res.append("null,");
        }
        res.deleteCharAt(res.length()-1); // 删除最末尾的一个逗号
        res.append("]");
        return res.toString();
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
     
        if(data.equals("[]")) return null; // 序列中无节点
        String[] vals = data.substring(1, data.length()-1).split(","); // 截取字符序列中括号内的部分,并按逗号分隔为多个字符串,转为字符数组。
        TreeNode root = new TreeNode(Integer.parseInt(vals[0])); // 创建根结点,节点值为序列中的第一个元素
        Queue<TreeNode> q = new LinkedList<> (); // 借助队列实现按层遍历构建树
        q.offer(root);
        int i = 1; // 借助一个指针指向结点在序列化列表中的对应索引位置,根结点已入队,这里初始化为第一个左子结点的位置即 vals[1]
        while(!q.isEmpty()) {
     
            TreeNode node = q.poll();
            if(!vals[i].equals("null")) {
      // 若当前节点的左子节点对应列表中索引位置上的值不为null,说明存在左子树
                node.left = new TreeNode(Integer.parseInt(vals[i]));
                q.offer(node.left);
            }
            i++;
            if(!vals[i].equals("null")) {
      // 若当前节点的右子节点对应列表中索引位置上的值不为null,说明存在右子树
                node.right = new TreeNode(Integer.parseInt(vals[i]));
                q.offer(node.right);
            }
            i++;
        }
        return root;
    }
}

// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.deserialize(codec.serialize(root));

解法二:前序遍历(DFS)

public class Codec {
     
//原理:我们可以根据前序遍历的顺序来序列化二叉树,因为前序遍历是从根节点开始的。在遍历二叉树碰到 null时,将其序列化为一个特殊的字符(如'$')
//另外,节点的数值之间要用一个特殊字符(如',')隔开,因为节点的值位数不定且正负不定。
//则下面二叉树      1               可以序列化为:
//				 /  \              [1,2,4,$,$,$,3,5,$,$,6,$,$]
//				2    3
//             /    / \
//            4    5   6
//我们接着以上述字符串为例分析如何反序列化二叉树。第一个读出的数字是1。由于前序遍历是从根节点开始的,这是根节点的值。
//接下来读出的数字是2,根据前序遍历的规则,这是根节点的左子节点的值。同样,接下来的数字4是值为2的节点的左子节点。
//接着从字符串里读出两个字符'$',这表明值为4的节点的左、右子节点均不存在,因此它是一个叶节点。接下来回到值为2的节点,重建它的右子节点。
//由于下一个字符是'$',这表明值为2的节点的右子节点不存在, 2这个节点的左、右子树都己经构建完毕,接下来回到根节点,反序列化根节点的右子树
//下一个序列化字符串中的数字是3,因此右子树的根节点的值为3。它的左子节点是一个值为5的叶节点,因为接下来的三个字符是"5,$,$"。
//同样,它的右子节点是值为6的叶节点,因为最后3个字符是"6,$,$"。

    int start=0;//注意这里必须是全局变量,否则后面的迭代过程中start无法正确变化
    public String serialize(TreeNode root) {
     
        if(root==null) return "[]";
        StringBuilder res = new StringBuilder();
        recur(root,res);
        res.deleteCharAt(res.length()-1); // 删除最后一个括号
        return res.toString();
    }
    public void recur(TreeNode root, StringBuilder res){
      // 前序遍历,递归实现
        if(root==null) {
      
            res.append("null,"); 
            return;
        }
        res.append(root.val+",");// 各元素间用,分割
        recur(root.left, res);
        recur(root.right, res);
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
     
        if(data.equals("[]")) return null; // Sting类型判断值是否相等不能用==,要用equal
        String[] inputs = data.split(",");
        //虽然data中以,结尾,但是上述分割后会默认最后一个,不存在, 不会使最后一个分割元素为空
        return build(inputs);
    }
    public TreeNode build(String[] inputs){
     
        TreeNode res;
        if(inputs[start].equals("null")){
      
            start++;
            return null; // 若当前节点为null,自然不存在左右节点了,直接返回
        }
        res = new TreeNode(Integer.parseInt(inputs[start]));
        start++;
        //注意:start不能以形参的形式引入build方法中,例如build(inputs,start);
        //原因是如果是这样,那下面 res.left = build(inputs,start); res.right = build(inputs,start+1); 会由于处于同一级迭代中,start值相同
        //但实际上 res.right中应该是上面 res.left迭代完成后才会执行的,start会+1,因此把start作为全局变量较为合适
        res.left = build(inputs);
        res.right = build(inputs);
        return res;
    }
}

参考

LeetCode题解1

你可能感兴趣的:(剑指offer,二叉树,leetcode)