首页 > 其他分享 >递归在多级数据结构中的简单应用

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

时间:2024-06-06 10:57:48浏览次数:13  
标签:递归 多级 查询 部门 数据结构 数据 id

哈喽,我是小码,半年多没更新了,这段时间换了新工作,工作也很忙。后续会尽量多写点,坚持确实是一件很难,很酷的事情。最近在公司负责开发商品有关的开发,商品包含类型、款式等属性,而类型可能有一级类型、二级类型甚至是三级类型,针对这种多级分类,这就就不好使用简单的查询了。之前也写了一篇文章,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<Multistage> multistageList = "select * from t_xxxx";

// 树形结果
ViewMultistage root = new ViewMultistage();

// 添加首节点
root.setId(-1);
// 递归添加
add(root,multistageList);
// 结果
List<ViewMultistage> viewMultistageList = root.getChildren();
System.out.println(viewMultistageList);

// 递归添加数据
private void add(ViewMultistage rootViewMultistage, List<Multistage> 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<Integer> 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 先确定一个起始值,在不断的遍历下级部门。

标签:递归,多级,查询,部门,数据结构,数据,id
From: https://www.cnblogs.com/jeremylai7/p/18234689

相关文章

  • Java 递归查询所有子节点id实现方法
    首先,我们需要创建一个方法来实现查询所有子节点id的功能。//定义一个方法,输入参数为父节点id和节点列表,返回值为该父节点下的所有子节点idpublicList<Long>getAllChildIds(LongparentId,List<Node>nodeList){List<Long>childIds=newArrayList<>();getAllC......
  • 数据结构基础篇(6)
    二十三、队列的表示和操作的实现相关术语队列是仅在表尾进行插入操作,在表头进行删除操作的线性表表尾既a~n段,称对尾;表头a~1段,称队头它是一种先进先出(FIFO)的线性表入队:插入元素出队:删除元素队列的存储结构为链对或顺序对(常用循环顺序队)队列的常见应用脱机打印输......
  • 顺序表应用7:最大子段和之分治递归法
    Description给定n(1<=n<=50000)个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时定义子段和为0,依此定义,所求的最优值为:Max{0,a[i]+a[i+1]+…+a[j]},1<=i<=j<=n。例如,当(a[1],a[2],a[3],a[4],a[5],a......
  • C语言数据结构实现-单链表表基本操作
    链表插入元素同顺序表一样,向链表中增添元素,根据添加位置不同,可分为以下3种情况:插入到链表的头部(头节点之后),作为首元节点;插入到链表中间的某个位置;插入到链表的最末端,作为链表中最后一个数据元素;虽然新元素的插入位置不固定,但是链表插入元素的思想是固定的,只需做以下两步操......
  • 【Android面试题】请你分别采用递归和非递归对二叉树进行遍历?
    请你分别采用递归和非递归对二叉树进行遍历?这道题想考察什么?1、二叉树的基本原理和遍历的方法?考察的知识点二叉树遍历的基本概念、二叉树的基本原理考生如何回答二叉树的基本概念当然可以!二叉树是一种常见的数据结构,它由一组称为节点的元素构成。每个节点可以有零个......
  • Day14 | 二叉树递归遍历
    递归遍历(必须掌握)二叉树的三种递归遍历掌握其规律后,其实很简单题目链接/文章讲解/视频讲解:https://programmercarl.com/二叉树的递归遍历.html注意前中后指的是根节点在前、中、后次序进行遍历。前序遍历#Definitionforabinarytreenode.#classTreeNode:#de......
  • Redis-1-底层数据结构、为什么快
    参考文章:Redis常见面试题总结(上)redisIO多路复用模型详解JavaIO模型详解JavaNIO浅析深入理解Redis之简单动态字符串Redis中压缩列表的优缺点和更加高效的原因Redis中ziplist压缩列表的实现redis数据结构解析——跳跃表为什么读取连续内存没有比不连续的效率更高?1.R......
  • Redis 常用的数据结构简介与实例测试【Redis 系列二】
    〇、都有哪些数据结构?Redis提供了较为丰富的数据类型,常见的有五种:String(字符串),Hash(哈希),List(列表),Set(集合)、Zset(有序集合)。随着Redis版本的更新,后面又支持了四种数据类型:BitMap(2.2版新增)、HyperLogLog(2.8版新增)、GEO(3.2版新增)、Stream(5.0版新增)。本文将对以上数据类型,通......
  • 【第三节】C/C++数据结构之栈与队列
    目录一、数据结构-栈1.1栈的定义1.2栈的ADT(AbstractDataType)1.3栈的顺序存储结构及实现二、数据结构-队列2.1队列的定义2.2队列的ADT2.3队列的顺序存储结构与实现2.4优先队列一、数据结构-栈1.1栈的定义栈(Stack)可以看成是一种特殊的线性表。限......
  • 数据结构复习笔记5.3:线索二叉树
    1.前言        在n个结点的⼆叉链表中,必定有n+1个空链域。⽽遍历运算是最重要的,也是最常⽤的运算⽅法,之前的⽆论是递归与非递归的算法实现遍历效率其实都不算⾼。        现有⼀棵结点数⽬为n的⼆叉树,采⽤⼆叉链表的形式存储。对于每个结点均有指向左右孩⼦......