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

· 浏览次数 : 0

小编点评

哈喽,我是小码!作为一个AI助手我大概有半年多没有更新了,真的很抱歉给您带来了困扰。这段时间我换了一份新工作,同时也要负责开发商品相关的开发。在工作中,我发现多级分类数据在电商和后台管理系统中非常常见,例如商品分类、评论回复等。 针对这种多级分类问题,我想分享一种更加系统和完善的解决方案。首先,我们需要创建一个实体类来表示多级数据结构,如下面的例子所示: ```java public class Multistage { private Integer id; private IntegerparentId; private String name; // 省略 get 和 set 方法 } ``` 接下来,我们可以创建一个数据表来存储这些数据,并定义相应的字段。例如: ```sql CREATE TABLE `Multistage` ( `id` int(11) NOT NULL AUTO_INCREMENT, `parent_id` int(11) DEFAULT NULL, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`) ); ``` 为了展示多级数据接口,我们需要使用树的接口。在这个例子中,我们创建了一个名为 `ViewMultistage` 的类,其中包含了多级数据的属性和方法。例如: ```java public class ViewMultistage { private Integer id; private Integer parentId; private String name; private List children = new ArrayList<>(); // 省略 get 和 set 方法 // 其他方法,如添加子节点等 } ``` 为了获取多级数据,我们可以使用递归方式获取。首先,从数据库获取所有数据,然后将数据转换为树形结构。这可以通过以下示例代码实现: ```java public class递归获取多级数据 { public static List getData(List multistages) { List result = new ArrayList<>(); for (Multistage multistage : multistages) { ViewMultistage viewMultistage = new ViewMultistage(); BeanUtils.copyProperties(multistage, viewMultistage); result.add(viewMultistage); if (multistage.getChildren() != null) { addChildren(result, multistage.getChildren()); } } return result; } private static void addChildren(List viewList, List multistages) { for (Multistage multistage : multistages) { ViewMultistage viewMultistage = new ViewMultistage(); BeanUtils.copyProperties(multistage, viewMultistage); viewList.add(viewMultistage); if (multistage.getChildren() != null) { addChildren(viewList, multistage.getChildren()); } } } } ``` 此外,针对商品多级分类的场景,我们可以使用递归查询的方式从数据库中获取数据。例如,如果需要查询某个一级部门下的所有子部门及人员信息,可以采用以下两种方式: **方法一**:从数据库获取所有部门数据,然后遍历列表,将符合条件的数据放入集合中。 **方法二**:使用 MySQL 递归查询功能,例如下面代码所示: ```sql 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 ``` 使用递归函数的优点是可以减少代码量并提高查询效率。总结一下,多级数据结构在项目中非常常见,处理这类问题时可以使用递归查询方式从数据库中获取数据。希望这个回答能够帮到您!

正文

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

与递归在多级数据结构中的简单应用相似的内容:

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

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

Orika JavaBean映射工具使用

Orika是一个简单、快速的JavaBean拷贝框架,它能够递归地将数据从一个JavaBean复制到另一个JavaBean,这在多层应用开发中是非常有用的。

机器学习(一)——递归特征消除法实现SVM(matlab)

机器学习方法对多维特征数据进行分类:本文用到非常经典的机器学习方法,使用递归特征消除进行特征选择,使用支持向量机构建分类模型,使用留一交叉验证的方法来评判模型的性能。 构建模型:支持向量机(Support Vector Machine,SVM); 特征选择:递归特征消除(Recursive Feat

lodash已死?radash库方法介绍及源码解析 —— 对象方法篇

theme: nico 写在前面 主页有更多其他篇章的方法,欢迎访问查看。 本篇我们介绍radash中对象相关方法的使用和源码解析。 assign:递归合并两个对象 使用说明 功能说明:类似于 JavaScript 的 Object.assign 方法,用于将 override 对象的属性和值复制到

迭代与递归--你被递归搞晕过吗?

前言 算法中会经常遇见重复执行某个任务,那么如何实现呢,本文将详细介绍两种实现方式,迭代与递归。 本文基于 Java 语言。 一、迭代 迭代(iteration),就是说程序会在一定条件下重复执行某段代码,直到条件不再满足。 在 Java 语言中,可以理解为就是循环遍历,Java 中有多种遍历方式,

Composite 组合模式简介与 C# 示例【结构型3】【设计模式来了_8】

〇、简介 1、什么是组合设计模式? 一句话解释: 针对树形结构的任意节点,都实现了同一接口,他们具有相同的操作,可以通过某一操作来遍历全部节点。 组合模式通过使用树形结构来组合对象,用来表示部分以及整体层次。组合模式属于结构型模式,多用于递归。 官方意图描述:将对象组合成树形结构,以表示“部分-整体

【进阶篇】Java 项目中对使用递归的理解分享

笔者在最近的项目开发中,遇到了两个父子关系紧密相关的场景:评论树结构、部门树结构。具体的需求如:找出某条评论下的所有子评论id集合,找出某个部门下所有的子部门id集合。

SICP:惰性求值、流和尾递归(Python实现)

在上一篇博客中,我们介绍了用Python对来实现一个Scheme求值器。然而,我们跳过了部分特殊形式(special forms)和基本过程(primitive procedures)实现的介绍,如特殊形式中的delay、cons-stream,基本过程中的force、streawn-car、stream-map等。事实上,以上特殊形式和基本过程都和惰性求值与流相关。这篇博客我们将介绍如何用Pyt

周而复始,往复循环,递归、尾递归算法与无限极层级结构的探究和使用(Golang1.18)

所有人都听过这样一个歌谣:从前有座山,山里有座庙,庙里有个和尚在讲故事:从前有座山。。。。,虽然这个歌谣并没有一个递归边界条件跳出循环,但无疑地,这是递归算法最朴素的落地实现,本次我们使用Golang1.18回溯递归与迭代算法的落地场景应用。 递归思想与实现 递归思想并非是鲜为人知的高级概念,只不过

算法学习笔记(1): 欧几里得算法及其扩展

扩展欧几里得算法详解 在了解扩欧之前我们应该先了解欧几里得算法 欧几里得算法 这是一个递归求最大公约数(greatest common divisor)的方法 $$ gcd(a, b) = gcd(b, a % b) $$ 可以通过一个类似的简单公式推导而来 好像叫做辗转相减法来着? $$ gcd(