题目:用java实现文本形式的树状结构图(如图所示要求用递归)

问题

用java实现文本形式的树状结构图(如图所示要求用递归)

在这里插入图片描述

方法1

代码

public class Test {
    public static void main(String[] args) {
        // 分类列表
        List<Category> categoryList = new ArrayList<>();
        // 一级分类
        Category oneCategory1 = new Category(1, 0, "Java工程师招聘试题", 1);
        // 二级分类
        Category twoCategory1 = new Category(2, 1, "Java面试题", 2);
        Category twoCategory2 = new Category(3, 1, "简答题", 2);
        Category twoCategory3 = new Category(4, 1, "Java面试题", 2);
        // 三级分类(属于“Java面试题”)
        Category threeCategory1 = new Category(5, 2, "选择题", 3);
        Category threeCategory2 = new Category(6, 2, "程序分析题", 3);
        // 四级分类(属于“选择题”)
        Category fourCategory1 = new Category(7, 5, "JS选择题", 4);
        // 五级分类(属于“JS选择题”)
        Category fiveCategory1 = new Category(8, 7, "HTML选择题", 5);
        Category fiveCategory2 = new Category(9, 7, "WebService选择题", 5);
        // 填充数据,最终实现categoryList就像是从数据库中查询出的集合这种效果
        categoryList.add(oneCategory1);
        categoryList.add(twoCategory1);
        categoryList.add(twoCategory2);
        categoryList.add(twoCategory3);
        categoryList.add(threeCategory1);
        categoryList.add(threeCategory2);
        categoryList.add(fourCategory1);
        categoryList.add(fiveCategory1);
        categoryList.add(fiveCategory2);

        // 往children中填充数据
        List<Category> list = build(categoryList);

        // 存储数据
        StringBuilder sb = new StringBuilder();

        // 将数据添加到可变字符串中
        add(sb, list, "");

        // 展示数据
        System.out.println(sb.toString());
    }

    /**
     * 将数据添加到可变字符串中
     *
     * @param sb     存储输出信息的可变字符串
     * @param list   分类列表
     * @param prefix 当前分类前面的修饰图形
     */
    private static void add(StringBuilder sb, List<Category> list, String prefix) {
        for (int i = 0; i < list.size(); i++) {
            // 对一级分类需要特殊处理
            if (list.get(i).getPid() == 0) {
                // 根据一级分类位置判断是否需要换行
                sb.append(i == 0 ? "" : "\n");
                // 对一级分类特殊处理
                sb.append(list.get(i).getName());
                // 添加子分类列表信息到可变字符串中
                add(sb, list.get(i).getChildren(), "\n ");
            } else {
                // 添加当前分类前面的修饰图形(不包括和当前分类最近的那个图形)
                sb.append(prefix);
                // 添加距离当前分类最近的那个图形
                sb.append(i < list.size() - 1 ? "├" : "└");
                // 添加当前分类名称
                sb.append(list.get(i).getName());

                // 子分类前面的修饰图形(不包括和子分类最近的那个图形)
                String sonPrefix = prefix + ((i == list.size() - 1) ? " " : "│");
                // 添加子分类列表信息到可变字符串中
                add(sb, list.get(i).getChildren(), sonPrefix);
            }
        }
    }

    /**
     * 构建多级分类菜单
     *
     * @param categoryList 所有的分类列表
     * @return
     */
    private static List<Category> build(List<Category> categoryList) {
        List<Category> collect = categoryList.stream()
                // 找出所有一级分类
                .filter(category -> category.getPid() == 0)
                // 向一级分类中填充孙子分类列表
                .map(category -> {
                    category.setChildren(getChildren(category, categoryList));
                    return category;
                })
                // 排序保证同级分类顺序不会改变
                .sorted((c1, c2) -> c1.getId().compareTo(c2.getId())).collect(Collectors.toList());
        return collect;
    }

    /**
     * 获取子分类列表
     *
     * @param category     当前分类
     * @param categoryList 所有的分类列表
     * @return
     */
    private static List<Category> getChildren(Category category, List<Category> categoryList) {
        List<Category> collect = categoryList.stream()
                // 通过过滤得到当前分类的所有子分类
                .filter(item -> item.getPid().equals(category.getId()))
                // 向当前子分类中填充孙子分类列表
                .map(item -> {
                    item.setChildren(getChildren(item, categoryList));
                    return item;
                })
                // 排序保证同级分类顺序不会改变
                .sorted((c1, c2) -> c1.getId().compareTo(c2.getId())).collect(Collectors.toList());
        return collect;
    }
}

class Category {
    /**
     * 分类id
     */
    private Integer id;

    /**
     * 父分类id
     */
    private Integer pid;

    /**
     * 分类名称
     */
    private String name;

    /**
     * 分类级别
     */
    private Integer level;

    /**
     * 当前分类的子分类
     */
    private List<Category> children;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getPid() {
        return pid;
    }

    public void setPid(Integer pid) {
        this.pid = pid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getLevel() {
        return level;
    }

    public void setLevel(Integer level) {
        this.level = level;
    }

    public List<Category> getChildren() {
        return children;
    }

    public void setChildren(List<Category> children) {
        this.children = children;
    }

    public Category(Integer id, Integer pid, String name, Integer level) {
        this.id = id;
        this.pid = pid;
        this.name = name;
        this.level = level;
    }
}

结果

在这里插入图片描述

方法2

代码

public class Father {

    private String title;

    private List<Father> children = new ArrayList<>();

    private Father(String title) {
        this.title = title;
    }

    private void addChild(Father f) {
        children.add(f);
    }

    private String toString(String self, String son) {
        StringBuilder b = new StringBuilder();
        // 为自己铺剩余前路
        b.append(self);
        // 为自己铺中间的路
        b.append(title);
        // 为自己铺后路
        b.append("\n");
        // 如果父亲(当前this)有儿子
        if (children.size() > 0) {
            // 如果只有一个儿子,不会执行for循环,或者为前n-1个儿子执行for循环
            for (int i = 0; i < children.size() - 1; i++) {
                // 为儿子铺部分前路
                b.append(son);
                // 前n-1个儿子将成为父亲,按照自己的意愿去为自己和儿子铺路
                b.append(children.get(i).toString("├", son + "│"));
            }

            // 如果只有一个儿子,直接执行下面的代码,或者为第n个儿子执行以下代码
            // 为儿子铺部分前路
            b.append(son);
            // 第n个儿子将成为父亲,按照自己的意愿去为自己和儿子铺路
            b.append(children.get(children.size() - 1).toString("└", son + " "));
        }
        return b.toString();
    }

    public static void main(String[] args) {
        // 0级菜单
        Father root = new Father("JAVA工程师招聘试题");

        // 1级菜单
        Father f1 = new Father("Java面试题");
        root.addChild(f1);

        // 2级菜单
        Father f1_1 = new Father("选择题");
        f1.addChild(f1_1);

        // 3级菜单
        Father f1_1_1 = new Father("JS选择题");
        f1_1.addChild(f1_1_1);

        // 4级菜单
        Father f1_1_1_1 = new Father("HTML选择题");
        f1_1_1.addChild(f1_1_1_1);

        // 4级菜单
        Father f1_1_1_2 = new Father("WebService选择题");
        f1_1_1.addChild(f1_1_1_2);

        // 2级菜单
        Father f1_2 = new Father("程序分析题");
        f1.addChild(f1_2);

        // 1级菜单
        Father f2 = new Father("简答题");
        root.addChild(f2);

        // 1级菜单
        Father f3 = new Father("编程题");
        root.addChild(f3);

        System.out.println(root.toString("", " "));
    }
}

结果

在这里插入图片描述

解释

提醒:下面的解释主要针对方法2,但是方法1的思想也是取自这种多级菜单思路

在这里插入图片描述
Father类中有children属性,这个属性是List<Father>类型的,然后children属性中的Father对象中又有children属性,所以和平时网站中的导航栏中菜单是类似的,因此我使用上面的图来表示,我们在main()方法中执行代码的最终结果就是上图这样子,所以困难并不在main()方法中,而在toString()中,在里面我把上级菜单比喻成父亲,而把子级菜单比喻成儿子,然后用现实生活中的"铺路"来给大家解释,我通过写注释的方式来给大家串一下代码,如下图:

在这里插入图片描述

其实这种问题我们平时编代码的时候也会遇到,不过不会遇见这种变态的,毕竟里面还有特殊符号,还有处理的方式也不一样,实在变态啊,我们拿爱奇艺举例:

在这里插入图片描述
如果你还看不出来,来看图:

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值