如何判断我们应该用前序还是中序还是后序遍历的框架?根据题意,思考一个二叉树节点需要做什么,到底用什么遍历顺序就清楚了。
652.寻找重复的子树
输入是一棵二叉树的根节点root
,返回的是一个列表
,里面装着若干个二叉树节点,这些节点对应的子树在原二叉树中是存在重复的。
分析:
解决此问题需要知道两点:
- 以root为根的节点二叉树长什么样?
- 以其他节点为根的二叉树长什么样?
- 让每个节点把自己序列化的结果存进去就知道是否重复了。借助一个
hashMap
解答:🚋(看答案出结果)
class Solution {
// 记录所有子树以及出现的次数
HashMap<String, Integer> memo = new HashMap<>();
// 记录重复的子树根节点
LinkedList<TreeNode> res = new LinkedList<>();
/* 主函数 */
List<TreeNode> findDuplicateSubtrees(TreeNode root) {
traverse(root);
return res;
}
/* 辅助函数 */
String traverse(TreeNode root) {
if (root == null) {
return "#";
}
String left = traverse(root.left);
String right = traverse(root.right);
String subTree = left + "," + right+ "," + root.val;
int freq = memo.getOrDefault(subTree, 0); //如果存在subTree就使用它的value,如果不存在就使用0
// 多次重复也只会被加入结果集一次
if (freq == 1) {
res.add(root);
}
// 给子树对应的出现次数加一
memo.put(subTree, freq + 1);
return subTree;
}
}
297.二叉树的序列化和反序列化(困难)
函数声明:
public class Codec {
// 二叉树-->字符串
public String serialize(TreeNode root) {
}
// 字符串--->二叉树
public TreeNode deserialize(String data) {
}
}
前序遍历
public class Codec {
String SEP = ",";
String NULL = "#";
/* 主函数,将二叉树序列化为字符串 */
public 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);
}
/* 主函数,将字符串反序列化为二叉树结构 */
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));
/***********************/
root.left = deserialize(nodes);
root.right = deserialize(nodes);
return root;
}
}
后序遍历
/* 主函数,将二叉树序列化为字符串 */
public 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;
}
serialize(root.left, sb);
serialize(root.right, sb);
/****** 后序遍历位置 ******/
sb.append(root.val).append(SEP);
/***********************/
}
反序列化:
deserialize 方法首先寻找 root 节点的值,然后递归计算左右子节点。
可见,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;
}
层序遍历
/**
* 二叉树的序列化
* 层序遍历
*/
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;i++){
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(!left.equals(NULL)){
parent.right = new TreeNode(Integer.parseInt(right));
q.offer(parent.right);
}else {
parent.right =null;
}
}
return root;
}