• 递归在多级数据结构中的简单应用


    哈喽,我是小码,半年多没更新了,这段时间换了新工作,工作也很忙。后续会尽量多写点,坚持确实是一件很难,很酷的事情。最近在公司负责开发商品有关的开发,商品包含类型、款式等属性,而类型可能有一级类型、二级类型甚至是三级类型,针对这种多级分类,这就就不好使用简单的查询了。之前也写了一篇文章,Java递归实现评论多级回复,写的比较简单。本文做一些更加系统、完善的方案。

    多级数据结构的场景

    多级数据在电商或者后台管理系统中是比较常见,比如博客或者短视频的评论,人员系统的多级部门等等,

    评论系统

    A 写了一条评论,B 就可以回复 A 评论,C 又可以回复 B.

    评论A
    ├── 评论B,回复A
    │   └── 评论C,回复B
    └── 评论D,回复A
    

    人员系统

    一般人员都是绑定一个企业和部门,企业有分公司,部门会多级部门。每个人员绑定一个部门.

    选择部门之后就可以获取对应的人员列表信息。

    商品的多级分类

    商品的类型,会细分多个类型,有一级分类,二级分类,或者有多级分类,比如黄金,分成黄金精品,黄金精品又分成手镯、项链。手镯又分成固口手镯、开口手镯、卡扣手镯等等。

    多级结构解决方案

    像这种类似于树形结构的数据,数据一般都会包含idparent_idname等字段,通过idparent_id来关联数据。下面是一个简单的实体:

    public class Multistage {
    
        public Multistage(Integer id,Integer parentId,String message) {
            this.id = id;
            this.parentId = parentId;
            this.message = message;
        }
    
        /**
         * id
         */
        private Integer id;
    
        /**
         * 父类id
         */
        private Integer parentId;
    
        /**
         * 名称
         */
        private String name;
        
        // 省略get、set
    
    }
    
    
    

    数据表和上面的实体类似。

    1、展示多级接口

    展示多级数据接口,就需要使用树的接口。类似下面的接口:

    public class ViewMultistage {
    
        /**
         * id
         */
        private Integer id;
    
        /**
         * 父类id
         */
        private Integer parentId;
    
        /**
         * 名称
         */
        private String name;
    
        /**
         * 子集
         */
        private List<ViewMultistage> children = new ArrayList<>();
    
    }
    

    上面的几个例子,评论、部门、以及商品的分类,都是需要展示所有数据,所以需要从数据库获取所有数据,再使用代码归类。

    针对这种多级、不确定层级数量的,一般都使用递归方法获取。从数据库获取到列表数据,使用递归方式转换:

    // 数据库获取数据
    List multistageList = "select * from t_xxxx";
    
    // 树形结果
    ViewMultistage root = new ViewMultistage();
    
    // 添加首节点
    root.setId(-1);
    // 递归添加
    add(root,multistageList);
    // 结果
    List viewMultistageList = root.getChildren();
    System.out.println(viewMultistageList);
    
    // 递归添加数据
    private void add(ViewMultistage rootViewMultistage, List multistageList) {
        for (Multistage multistage : multistageList) {
            if (rootViewMultistage.getId().equals(multistage.getParentId())) {
                ViewMultistage viewMultistage = new ViewMultistage();
                BeanUtils.copyProperties(multistage, viewMultistage);
                rootViewMultistage.getChildren().add(viewMultistage);
                add(viewMultistage, multistageList);
            }
        }
    }
    

    简略解释一下代码:

    • 获取数据列表,创建树形类。
    • 创建 -1 的虚拟节点,添加到属树形类上。
    • 使用递归不断找到子节点,直到遍历完所有数据。
    • 虚拟节点的子节点 root.getChildren() 就是我们要的结果。

    从数据库获取的数据一定要有顺序,子数据不能比父数据排前面。

    查询子集数据

    如果不是获取所有的数据,只是获取一部分的数据。以部门和人员举例,部门有子部门,子部门有部门人员。要求查询部门以及子部门的人员信息。商品绑定二级款式或者三级款式,但是需要通过一级款式找到对应的下级的商品。

    解决问题的重点就是获取到部门以及子部门的集合,这里就有两个方案。

    获取所有部门,代码筛选

    从数据获取所有的部门数据,遍历列表,筛选出符合条件的数据。比如上面的华南部门,假设 id 为 3,通过如下代码就能获取到部门 id 集合:

    Integer id = 3;
    Set idSet = new HashSet<>();
    idSet.add(id);
    for (ViewMultistage multistage : viewMultistageList) {
        Integer parentId = multistage.getParentId();
        if (idSet.contains(parentId)) {
            idSet.add(multistage.getId());
        }
    }
    

    通过某个 id 就可以筛选部门以及子部门的集合,然后通过部门 id 的集合就能获取到人员信息。

    但是如果只是获取一小部门的子集,却要获取全部数据,有点浪费查询资源。可以在数据库中刷选好数据,通过数据库递归获取数据。

    通过数据库递归查找

    查询数据大部分时候还是需要查询数据库,通过数据库一步到位,也减少了代码的的书写。将递归的查询方式转移到数据库中,使用到了 Mysql 递归函数 with recursive,用法如下:

    WITH RECURSIVE family_tree AS (
    
        -- 基础查询:从给定的一级 id 开始
        SELECT
            id,
            parent_id
        FROM parents
        WHERE id = 1  -- 替换为你要查询的一级 id
        
        UNION ALL
        -- 递归查询:查找子节点
        SELECT
            p.id,
            p.parent_id
        FROM
            parents p
        INNER JOIN family_tree ft ON p.parent_id = ft.id
    
    )
    
    select tu.* from family_tree ft
    left join t_user tu on tu.dept_id = ft.id
    

    分析with recursive用法:

    • WITH RECURSIVE family_tree AS 是一个固定用法,将 () 查询的数据结果返回给 family_tree。
    • 函数体里面包含两部分,起始值循环值
      • 起始值:确认首节点的位置,上面例子以 id = 1作为起始值。
      • 循环值:以 id = 1 找到 parent_id 为 1 的数据,假设这个 id 为 2,然后找到 parent_id 的数据,一直循环查找,直到查询结束。
      • 上面的 family_tree 就类似通过父部门获取到所有的子部门,使用用户表关联,就能获取所有部门以及子部门的用户信息。

    总结

    多级数据结构在项目开发中是一种比较常见的数据结构,比如:

    • 评论,评论回复评论,其他评论再回复评论。
    • 企业部门,部门有子部门,或者公司有子公司。
    • 商品分类有一级分类,二级分类。

    多级数据结构需要解决两个问题,一个是查询所有数据,另外一个是查询部分数据,这两个都需要使用到递归的查询。

    • 查询所有数据
      • 比较少的数据,就可以查询所有的数据,从数据库查询到所以数据,按照添加时间的先后排序,再使用递归的方式将数据组装成一个树形结构。
      • 如果是评论的数据,就需要存储评论的等级,先分页获取一级的评论,再通过一级评论获取下级评论。
    • 查询部分数据
      • 通过某个一级部门获取所有的子部门,有两种方式,第一种是获取所有的部门数据,再循环遍历数据,将符合的数据放到一个集合中。
      • 第二种是使用 Mysql 递归,使用with recursive 先确定一个起始值,在不断的遍历下级部门。
  • 相关阅读:
    RocketMQ存储设计的奥妙
    基于JAVA婚纱影楼服务管理计算机毕业设计源码+系统+mysql数据库+lw文档+部署
    自定义mvc框架复习
    使用@Transactional注解,Exception异常没有回滚解决方案
    [附源码]计算机毕业设计贵港高铁站志愿者服务平台
    嵌入式面经111题答案汇总(含技术答疑)_嵌入式项目源码分享
    深入理解迪米特法则(Law Of Demeter)
    Symbol详解
    2023年下学期《C语言》作业0x02-分支 XTU OJ 1068 1069 1070 1071 1072
    牛客网刷题【BC33、BC56、BC44、BC91、BC49、写函数求最大值】
  • 原文地址:https://www.cnblogs.com/jeremylai7/p/18234689