一、lbc652
给定一棵二叉树,返回所有重复的子树。对于同一类的重复子树,你只需要返回其中任意一棵的根结点即可。
两棵树重复是指它们具有相同的结构以及相同的结点值。
贝壳笔试改编:找一棵树中最大同构子树,同构表示两棵树结构相同;(猜测:因为输入只有1和#,所以和上面代码一样,只是返回值不同,若输入正常(不只有1),可直接使
String serial = 1 + ","
+ collect(node.left) + ","
+ collect(node.right);
输入{1,1,1,1,#,1,#}
输出 2
package zsh;
import java.util.*;
import java.util.stream.Collectors;
public class IsDuplicateSubtree {
public static class TreeNode{
int val;
TreeNode left;
TreeNode right;
public TreeNode(int data){
this.val = data;
}
}
Map<String, Integer> count;
List<TreeNode> ans;
public List<TreeNode> findDuplicateSubtrees(TreeNode root) {
count = new HashMap();
ans = new ArrayList();
collect(root);
return ans;
}
int sum = 0;
public String collect(TreeNode node) {
if (node == null) return "#";
String serial = 1 + "," //同构子树
// String serial = node.val + "," //相同子树
+ collect(node.left) + ","
+ collect(node.right); //从最左子节点开始,前序
count.put(serial, count.getOrDefault(serial, 0) + 1);
if (count.get(serial) == 2) {
ans.add(node);
String[] strArr = serial.split(",");
int length = 0;
for(String s : strArr){
if(!s.equals("#")){
length++;
}
}
// int lengths = Arrays.stream(serial.split(",")).filter(s->Character.isDigit(s.toCharArray()[0])).collect(Collectors.toList()).size();
sum = Math.max(sum, length); //求最大同构子树节点数量
}
return serial;
}
public static void main(String[] args) {
IsDuplicateSubtree zsh = new IsDuplicateSubtree();
TreeNode root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(1);
root.left.right = new TreeNode(4);
root.left.left.left = new TreeNode(4);
root.right.left = new TreeNode(2);
root.right.right = new TreeNode(4);
root.right.left.left = new TreeNode(4);
zsh.findDuplicateSubtrees(root);
System.out.println(zsh.sum);
}
}
二、目录树![](https://img-blog.csdnimg.cn/20181206213649566.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMjMxOTI2,size_16,color_FFFFFF,t_70)
参考https://blog.csdn.net/qq_41231926/article/details/84865683
知识点:深度优先遍历
思路:先建树,再深度优先遍历
用结构体file来作为树的节点,其中包含char name[261]存储文件名,bool型变量isDirectory表示是目录还是文件,bool型变量visited用来表示dfs过程中该文件节点是否被访问过,vector<file>型变量subFiles存储其子目录。
题目给的每个字符串,表示的都是从root的第一个孩子开始的路径。因此,我们用一个vector<file>型变量files存储每个字符串中的文件值,依次将files中的文件通过函数void add(file &f, int index)添加进以root为根的树中。
void add(file &f, int index)函数的实现如下:insert(int cur, List<File> sub)
注意,这个过程中我们会改变f文件的subFile值,所以需要传递引用。
(1)如果index == files.size(),说明已经遍历完了files中的所有文件,直接return。
(2)遍历f的子目录,寻找与files[index]名字相同且同是文件或同是目录的文件,如果找到,说明files[index]已经在f的子目录中,我们考虑第index + 1个文件,即递归地调用该函数add(f.subFiles[i], index + 1),并且返回。
(3)如果(2)过程没有在f的子目录中寻找到files[index],将files[index]加入f的子孩子中,并且递归地调用该函数add(f.subFiles[f.subFiles.size() - 1], index + 1)。
从root节点开始深度优先遍历这棵文件树,并用int型level变量记录空格数。
void dfs(file &nowVisit, int level)函数的实现如下:
注意,这个过程中,我们会对f文件的subFile进行排序,所以需要传递引用。
(1)如果当前文件还未被访问过,标记其已被访问,并且对其subFile进行排序。
(2)输出当前空格数和文件名。
(3)对其subFile里的文件,递归调用dfs函数,注意level需加2。
时间复杂度和空间复杂度均与题给的数据有关,很难计算。
7
b
c\
ab\cd
a\bc
ab\d
a\d\a
a\d\z\
package zsh;
import java.util.*;
public class DirectoryTree {
public static class File{
String name;
boolean isDirectory, visited;
List<File> subfiles = new ArrayList<>(); //超子树
public File(String n){
this.name = n;
}
}
static List<File> listFile = new ArrayList<>();
public static void insert(int cur, List<File> sub){ //插入节点
if(cur == listFile.size()){
return;
}
for (int i = 0; i < sub.size(); i++) { //子树有节点,依次判断,是否已经存在
if(listFile.get(cur).name.equals(sub.get(i).name)
&& listFile.get(cur).isDirectory == sub.get(i).isDirectory){
insert(cur + 1, sub.get(i).subfiles); //递归下一个文件
return;
}
}
sub.add(listFile.get(cur)); //子树中不存在该节点
insert(cur+1, sub.get(sub.size()-1).subfiles); //下一个节点是当前节点的子节点,插入到当前节点的子树中
}
public static void dfs(File file, int level){
if(!file.visited){
file.visited = true;
Collections.sort(file.subfiles, new Comparator<File>() {
@Override
public int compare(File o1, File o2) {
if (o1.isDirectory && !o2.isDirectory) { //目录优先
return -1;
}
else if (!o1.isDirectory && o2.isDirectory) {
return 1;
}
else {
return o1.name.compareTo(o2.name);
}
}
});
//打印,level空格数 缩进
StringBuilder builder = new StringBuilder();
for (int i = 0; i < level; i++) {
builder.append(" ");
}
builder.append(file.name);
System.out.println(builder.toString());
for(File subfile : file.subfiles){
dfs(subfile, level+2);
}
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int num = Integer.parseInt(scanner.nextLine().trim());
File root = new File("root");
root.isDirectory = true;
root.visited = false;
for (int i = 0; i < num; i++) {
String str = scanner.nextLine();
String[] strArr = str.split("\\\\");
listFile.clear(); //清空listFile
for (int j = 0; j < strArr.length; j++) { //读入一行目录 依次加入listFile
File f = new File(strArr[j]);
if(str.charAt(str.length()-1) != '\\' && j == strArr.length -1){
f.isDirectory = false;
}
else{
f.isDirectory = true;
}
f.visited = false;
listFile.add(f);
}
insert(0, root.subfiles); //建树
}
dfs(root, 0); //遍历树
System.out.println("end");
}
}