二叉树学习指南
前置芝士
二叉树创建
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
前序遍历二叉树的序列化
[problem description]
public class Codec {
// 把一棵二叉树序列化成字符串
public String serialize(TreeNode root) {}
// 把字符串反序列化成二叉树
public TreeNode deserialize(String data) {}
}
序列化:2,1,#,6,3,#,#
[solved]
[java]
public class Main{
// 代表分隔符的字符
String SEP=",";
// 代表 null 空指针的字符
String NULL="#";
// 用于拼接字符串
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);
}
}
前序遍历二叉树的反序列化
[problem description]
String data = "1,2,#,4,#,#,3,#,#,";
String[] nodes = data.split(",");
[solved]
单单前序遍历结果是不能还原二叉树结构的,因为缺少空指针的信息,至少要得到前、中、后序遍历中的两种才能还原二叉树。但是这里的 node 列表包含空指针的信息,所以只使用 node 列表就可以还原二叉树。
public class Main{
// 代表分隔符的字符
String SEP=",";
// 代表 null 空指针的字符
String NULL="#";
TreeNode deserialize(String data){
LinkedList<String> nodes=new LinkedList<>();
for(String s:data.split(SEP)){
nodes.addLast(s);
}
return dfs(nodes);
}
TreeNode dfs(LinkedList<String> nodes){
if(nodes.isEmpty()) return null;
String node=nodes.removeFirst();
if(node.equals(NULL)) return null;
TreeNode root=new TreeNode(Integer.parseInt(node));
root.left=dfs(nodes);
root.right=dfs(nodes);
return root;
}
}
后序遍历序列化
[solved]
/* 辅助函数,将二叉树存入 StringBuilder */
void serialize(TreeNode root, StringBuilder sb) {
if (root == null) {
sb.append(NULL).append(SEP);
return;
}
serialize(root.left, sb);
serialize(root.right, sb);
/****** 后序遍历位置 ******/
sb.append(root.val).append(SEP);
/***********************/
}
后序遍历反序列化
[problem description]
[solved]
root 的值是列表的最后一个元素。我们应该从后往前取出列表元素,先用最后一个元素构造 root,然后递归调用生成 root 的左右子树。注意,根据上图,从后往前在 nodes 列表中取元素,一定要先构造 root.right 子树,后构造 root.left 子树。
/* 主函数,将字符串反序列化为二叉树结构 */
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;
}
中序遍历序列化
[solved]
中序遍历无法反序列化,因为找不到root
/* 辅助函数,将二叉树存入 StringBuilder */
void serialize(TreeNode root, StringBuilder sb) {
if (root == null) {
sb.append(NULL).append(SEP);
return;
}
serialize(root.left, sb);
/****** 中序遍历位置 ******/
sb.append(root.val).append(SEP);
/***********************/
serialize(root.right, sb);
}
层序遍历序列化
[problem description]
[solved]
每一个非空节点都会对应两个子节点,那么反序列化的思路也是用队列进行层级遍历,同时用索引 i 记录对应子节点的位置
/* 将字符串反序列化为二叉树结构 */
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;
}