实现一个类:
public class Codec {
// 把一棵二叉树序列化成字符串
public String serialize(TreeNode root) {}
// 把字符串反序列化成二叉树
public TreeNode deserialize(String data) {}
}
比如:
serialize
方法也许会把它序列化成字符串 2,1,#,6,3,#,#
,其中 #
表示 null
指针,那么把这个字符串再输入 deserialize
方法,依然可以还原出这棵二叉树
递归遍历方式有前序遍历,中序遍历,后序遍历;迭代方式一般是层级遍历。
1、前序遍历:基本框架如下:
void traverse(TreeNode root) {
if (root == null) return;
// 前序遍历的代码,前序先遍历root节点
traverse(root.left);
traverse(root.right);
}
伪码可以写成如下-
LinkedList<Integer> res;
void traverse(TreeNode root) {
if (root == null) {
// 用 -1 代表空指针 null
res.addLast(-1);
return;
}
/****** 前序遍历位置 ***遍历完之后加入链表***/
res.addLast(root.val);
traverse(root.left);
traverse(root.right);
}
若写成字符串形式:
// 分隔符的字符
String SEP = ",";
// 代表 null 空指针的字符
String NULL = "#";
// StringBuilder 可以用于高效拼接字符串
StringBuilder sb = new StringBuilder();
/* 将二叉树打平为字符串 */
void traverse(TreeNode root, StringBuilder sb) {
if (root == null) {
sb.append(NULL).append(SEP);
return;
}
/****** 前序遍历 ***遍历完之后加入列表***/
sb.append(root.val).append(SEP);
traverse(root.left, sb);
traverse(root.right, sb);
}
序列化函数 serialize
的代码:
String SEP = ",";
String NULL = "#";
/* 主函数,将二叉树序列化为字符串 */
String serialize(TreeNode root) {
StringBuilder sb = new StringBuilder();
serialize(root, sb);
return sb.toString();
}
/* 辅助函数,将二叉树存入 StringBuilder */
void serialize(TreeNode root, StringBuilder sb) {
if (root == null) {
sb.append(NULL).append(SEP);
return;
}
/****** 前序遍历位置 ******/
sb.append(root.val).append(SEP);
/***********************/
serialize(root.left, sb);
serialize(root.right, sb);
}
deserialize
函数,将字符串反过来构造二叉树:
//字符串转换成列表
String data = "1,2,#,4,#,#,3,#,#,";
String[] nodes = data.split(",");
先确定根节点 root,然后遵循前序遍历的规则,递归生成左右子树即可:
/* 主函数,将字符串反序列化为二叉树结构 */
TreeNode deserialize(String data) {
// 将字符串转化成列表
LinkedList<String> nodes = new LinkedList<>();
for (String s : data.split(SEP)) {
nodes.addLast(s);
}
return deserialize(nodes);
}
/* 辅助函数,通过 nodes 列表构造二叉树 */
TreeNode deserialize(LinkedList<String> nodes) {
if (nodes.isEmpty()) return null;
/****** 前序遍历位置 ******/
// 列表最左侧就是根节点
String first = nodes.removeFirst();
if (first.equals(NULL)) return null;
TreeNode root = new TreeNode(Integer.parseInt(first));
/**********根据树的递归性质,nodes 列表的第一个元素就是一棵树的根节点,所以只要将列表的第一个元素取出作为根节点,剩下的交给递归函数去解决即可。*************/
root.left = deserialize(nodes);
root.right = deserialize(nodes);
return root;
}
后续遍历法:
基本框架与前序遍历一样,顺序稍微改变:
void traverse(TreeNode root) {
if (root == null) return;
traverse(root.left);
traverse(root.right);
// 后序遍历的代码
}
/***************后续跟前序节点位置不同,首先需要找到root**************/
/* 主函数,将字符串反序列化为二叉树结构 */
TreeNode deserialize(String data) {
LinkedList<String> nodes = new LinkedList<>();
for (String s : data.split(SEP)) {
nodes.addLast(s);
}
return deserialize(nodes);
}
/* 辅助函数,通过 nodes 列表构造二叉树 */
TreeNode deserialize(LinkedList<String> nodes) {
if (nodes.isEmpty()) return null;
// 从后往前取出元素
String last = nodes.removeLast();
if (last.equals(NULL)) return null;
TreeNode root = new TreeNode(Integer.parseInt(last));
// 限构造右子树,后构造左子树
root.right = deserialize(nodes);
root.left = deserialize(nodes);
return root;
}
中序遍历不好确定root的位置,所以不好定位,无法进行反序列化;
层级遍历:
String SEP = ",";
String NULL = "#";
/* 将二叉树序列化为字符串 */
String serialize(TreeNode root) {
if (root == null) return "";
StringBuilder sb = new StringBuilder();
// 初始化队列,将 root 加入队列
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while (!q.isEmpty()) {
TreeNode cur = q.poll();
/* 层级遍历代码位置 */
if (cur == null) {
sb.append(NULL).append(SEP);
continue;
}
sb.append(cur.val).append(SEP);
/*****************/
q.offer(cur.left);
q.offer(cur.right);
}
return sb.toString();
}
/* 将字符串反序列化为二叉树结构 */
TreeNode deserialize(String data) {
if (data.isEmpty()) return null;
String[] nodes = data.split(SEP);
// 第一个元素就是 root 的值
TreeNode root = new TreeNode(Integer.parseInt(nodes[0]));
// 队列 q 记录父节点,将 root 加入队列
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
for (int i = 1; i < nodes.length; ) {
// 队列中存的都是父节点
TreeNode parent = q.poll();
// 父节点对应的左侧子节点的值
String left = nodes[i++];
if (!left.equals(NULL)) {
parent.left = new TreeNode(Integer.parseInt(left));
q.offer(parent.left);
} else {
parent.left = null;
}
// 父节点对应的右侧子节点的值
String right = nodes[i++];
if (!right.equals(NULL)) {
parent.right = new TreeNode(Integer.parseInt(right));
q.offer(parent.right);
} else {
parent.right = null;
}
}
return root;
}
标准层级遍历:
while (!q.isEmpty()) {
TreeNode cur = q.poll();
if (cur.left != null) {
q.offer(cur.left);
}
if (cur.right != null) {
q.offer(cur.right);
}
}