算法小抄学习笔记 — 3.二叉树序列化与反序列化

本文介绍二叉树的序列化与反序列化方法,包括先序、中序、后序及层次遍历的序列化和反序列化过程,并提供了具体的实现代码。

1 二叉树的序列化与反序列化

  • 二叉树的序列化是指将二叉树转换为唯一标识的字符串,反序列化是指将字符串转换为相应的二叉树。

  • 二叉树可以使用先序、中序、后序和层次遍历进行序列化,可以使用先序、后序和层次遍历进行反序列化。(中序遍历不可以反序列化)

  • 如何判断两个二叉树相等?可能想法就是遍历二叉树,依次比较节点是否相等,这是没有问题的。但是如何判断子树中是否存在相等的树呢?可能就需要将所有子树序列化,加入一个map集合,从而进行判断。652 寻找重复的子树,就使用了这个思想,将在下一笔记中总结此题。




2 先序遍历解法

序列化,对于某一节点

  • 若为null,则在字符串中加入#来表示空树。

  • 否则,在字符串中加入该值。

/* 前序二叉树序列化 */
private String preSerialize(TreeNode root) {
    StringBuilder sb = new StringBuilder();
    preSerialize(root, sb);
    return sb.toString();
}

/* 辅助函数,将二叉树存入StringBuilder */
private void preSerialize(TreeNode root, StringBuilder sb) {
    if (root == null) {
        sb.append("#").append(",");
        return;
    }
    /* ---- 先序遍历代码 ---- */
    sb.append(root.val).append(",");
    /* ------------------- */
    
    preSerialize(root.left, sb);
    preSerialize(root.right, sb);
}

反序列化,先将字符串转换为nodes数组,利用数组中第一个元素为根节点这个性质,反序列化,建立二叉树。

/* 前序二叉树反序列化 */
private TreeNode preDeserialize(String data) {
	Deque<String> nodes = new LinkedList<>();
	for (String s : data.split(",")) {
		nodes.addLast(s);
	}
	return preDeserialize(nodes);
}

private TreeNode preDeserialize(Deque<String> nodes) {
	// base case
    if (nodes.isEmpty()) {
		return null;
	}

    /* ---- 先序遍历代码 ---- */
	String node = nodes.removeFirst();
	if (node.equals("#")) {
		return null;
	}
	TreeNode root = new TreeNode(Integer.parseInt(node));
	/* -------------------- */
    
	root.left = preDeserialize(nodes);
	root.right = preDeserialize(nodes);
	return root;
}




3 中序遍历解法

序列化,其实与先序遍历思路一致,只是处理代码换了一个位置。

/* 中序遍历序列化 */
private String inSerialize(TreeNode root) {
    StringBuilder sb = new StringBuilder();
    inSerialize(root, sb);
    return sb.toString();
}

/* 辅助函数,将二叉树root序列化为StringBuilder */
private void inSerialize(TreeNode root, StringBuilder sb) {
    if (root == null) {
        sb.append("#").append(",");
        return;
    }

    inSerialize(root.left, sb);

    /* ---- 中序遍历代码 ---- */
    sb.append(root.val).append(",");
    /* ------------------- */

    inSerialize(root.right, sb);
}

反序列化,由于从中序遍历数组中无法知道根节点的位置,所以无法进行反序列化。




4 后序遍历解法

序列化,与先序遍历思路基本一致。

/* 后序遍历序列化 */
private String postSerialize(TreeNode root) {
    StringBuilder sb = new StringBuilder();
    postSerialize(root, sb);
    return sb.toString();
}

/* 辅助函数,将二叉树root后序序列化为StringBuilder */
private void postSerialize(TreeNode root, StringBuilder sb) {
    if (root == null) {
        sb.append("#").append(",");
        return;
    }

    postSerialize(root.left, sb);
    postSerialize(root.right, sb);

    /* ---- 后序遍历代码 ---- */
    sb.append(root.val).append(",");
    /* ------------------- */
}

反序列化,这里要注意,后序遍历数组中最后一个节点才是根节点,所以处理时应该先生成右子树,再生成左子树。

/* 后序遍历反序列化 */
private TreeNode postDeserialize(String data) {
    LinkedList<String> nodes = new LinkedList<>();
    for (String s : data.split(",")) {
        nodes.addLast(s);
    }
    return postDeserialize(nodes);
}

/* 辅助函数,将LinkedList<String>后序反序列化为TreeNode */
private TreeNode postDeserialize(LinkedList<String> nodes) {
    if (nodes.isEmpty()) {
        return null;
    }

    String node = nodes.removeLast();
    if (node.equals("#")) {
        return null;
    }

    TreeNode root = new TreeNode(Integer.parseInt(node));
    // 先构造右子树,再构造左子树
    root.right = postDeserialize(nodes);
    root.left = postDeserialize(nodes);

    return root;
}




5 层次遍历解法

序列化,像BFS那样,使用一个结点队列,在while中依次出队

  • 若为null,则在字符串后加上"#",并continue。

  • 否则,在字符串后加上相应的值,然后在该节点的左右结点依次入队。

/* 层次遍历序列化 */
private String hierSerialize(TreeNode root) {
    if (root == null) return "";
    StringBuilder sb = new StringBuilder();
    Queue<TreeNode> q = new LinkedList<>();
    q.offer(root);

    while (!q.isEmpty()) {
        TreeNode cur = q.poll();
        if (cur == null) {
            sb.append("#").append(",");
            continue;
        }
        sb.append(cur.val).append(",");

        q.offer(cur.left);
        q.offer(cur.right);
    }

    return sb.toString();
}

反序列化,使用层次遍历反向建立二叉树。

/* 层次遍历反序列化 */
private TreeNode hierDeserialize(String data) {
    if (data.isEmpty()) return null;
    String[] nodes = data.split(",");

    // 第一个元素为root
    TreeNode root = new TreeNode(Integer.parseInt(nodes[0]));

    // 队列q记录父节点,将root加入队列
    Queue<TreeNode> q = new LinkedList<>();
    q.offer(root);

    int i = 1;
    while(i < nodes.length) {
        TreeNode parent = q.poll();

        // 父节点对应左侧的节点
        String left = nodes[i++];
        if (!left.equals("#")) {
            parent.left = new TreeNode(Integer.parseInt(left));
            q.offer(parent.left);
        } else {
            parent.left = null;
        }

        // 父节点右侧的节点
        String right = nodes[i++];
        if (!right.equals("#")) {
            parent.right = new TreeNode(Integer.parseInt(right));
            q.offer(parent.right);
        } else {
            parent.right = null;
        }
    }
    return root;
}




6 相关题目




7 参考

  1. 二叉树序列化的题,就那几个框架,枯燥至极
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值