树(最大同构子树,目录树)

一、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://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");
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值