基础算法
数据库
Javascript
HTML 编程
SVG
附录
原始需求是前端同学在 VUE 开发中使用 ElementUI 的树形组件时,组件需要一次性将整个树形组件的数据全部加载,这就要求后端将数据一次性返回并且必须是树形数据结构。我是理解不了 ElementUI 组件的逻辑,我自己的树形组件可以按需加载,即点击树中某一个节点时才加载这个节点下的所有数据。反抗是没有用的,老实实现需求就是了。
查询数据表的 SQL 语句如下:
这个表通过字段parent_project_id
实现了树形目录中的上下级关系,从而可以实现无限级的树形数据结构。顶层目录的parent_project_id
值为0
。
要生成的树形目录数据结构应该是这样的(实际上远比这个要复杂得多):
使用children
属性来保存子节点的所有信息,没有子节点时那么children
则为空数组。
开始想如何实现,第一种方法是广度优先搜索,一层一层遍历节点和子节点,直到最后一层的所有节点都没有子节点;第二种方法是深度优先搜索,查找每个节点的第一个子节点是否有子节点是否有数据,如果有则继续向下查找,如果没有,则跳到下一个同胞节点向下查找,直到所有节点都没有子节点为止。记得上人工智能课时老师还留了这道题当作业,好吧,这两个方法都不可取,因为每一次查找都要访问一次数据库,即使不访问数据库,在内存中遍历也是一件不小的工程。我当时的作业每一种算法都有上百行代码,这显然作为一个有强迫症的程序员是不能容忍的。
开始思考第三种算法,一次性从数据库获取数据,且要求在内存中的查找数据的次数尽可能少,即时间复杂度尽可能低。最后尝试使用 引用类型的特性 实现,下面是的代码(Scala):
以上代码的解释如下:
primaryColumn
表示主键的名字,本例中为project_id
;parentColumn
表示父级列的列名,本例中为parent_project_id
;startPoint
为查询起始点,本例中为0
,用这个值来确认是否为顶层项;newColumn
为生成新列的名称,用于保存所有子节点数据,本例中为children
。this
即表示当前表格,this.rows
表示当前表格的所有行。relations
保存每一项的实体系,是一个 HashMap 结构。Key 是节点主键project_id
的值,Value 则是实体,包含节点的所有属性。result
保存最终要返回的结果,是一个对象数组。stash
中。map
变量即每一个实体。如果是顶层那么就添加到result
中,如果是子项则在relations
中找到父级实体并添加为这个实体的子项。由于引用类型的特性,result
和relations
中引用的实体是同一个,如果更新了relations
中的实体,那么result
中的保存的值也跟着更新了。SELECT id AS project_id, parent_project_id, project_name FROM qross_projects ORDER BY parent_project_id ASC, id ASC;
result
则包含了所有顶层的实体,顶层实体中包含了自己的所有子项。由于引用类型的特性,虽然三个集合中项都指向相同的实体,但清空relations
和stash
并不影响reulst
的结果。本例是 PQL 的 Sharp 表达式 TO TREE
的底层实现。
参考链接