假设您有一个存储有序树层次结构的平面表:
Id Name ParentId Order
1 'Node 1' 0 10
2 'Node 1.1' 1 10
3 'Node 2' 0 20
4 'Node 1.1.1' 2 10
5 'Node 2.1' 3 10
6 'Node 1.2' 1 20
这是一个图,我们有[id] Name
。 根节点0是虚构的。
[0] ROOT / \ [1] Node 1 [3] Node 2 / \ \ [2] Node 1.1 [6] Node 1.2 [5] Node 2.1 / [4] Node 1.1.1
您将使用哪种简约方法将其作为正确排序,正确缩进的树输出到HTML(就此而言,还是文本)?
进一步假设您只有基本的数据结构(数组和哈希图),没有带有父/子引用的奇特对象,没有ORM,没有框架,只有两只手。 该表表示为结果集,可以随机访问。
可以使用伪代码或简单的英语,这纯粹是一个概念性问题。
额外的问题:在RDBMS中是否存在从根本上更好的方法来存储这样的树结构?
编辑和添加
要回答一个评论者( Mark Bessey )的问题:根节点不是必需的,因为它永远不会显示。 ParentId = 0是表示“这些是顶级”的约定。 Order列定义如何对具有相同父代的节点进行排序。
我所说的“结果集”可以图片为一个哈希表数组(保留在该术语中)。 对于我的示例,本应已经存在。 一些答案需要付出额外的努力,然后再进行构建,但这没关系。
这棵树可以任意深。 每个节点可以有N个子节点。 不过,我并没有真正想到“成千上万的条目”树。
不要将我对节点命名(“节点1.1.1”)的选择误认为是要依赖的东西。 这些节点也可以称为“ Frank”或“ Bob”,没有暗示命名结构,这仅仅是为了使其可读。
我已经发布了自己的解决方案,因此你们可以将它分解成碎片。
#1楼
考虑将noeo工具(例如neo4j)用于层次结构。 例如,诸如linkedin之类的联网应用程序使用couchbase(另一个nosql解决方案)
但是,仅将nosql用于数据集市级别的查询,而不用于存储/维护事务
#2楼
假设您知道根元素为零,这是要输出到文本的伪代码:
function PrintLevel (int curr, int level)
//print the indents
for (i=1; i<=level; i++)
print a tab
print curr \n;
for each child in the table with a parent of curr
PrintLevel (child, level+1)
for each elementID where the parentid is zero
PrintLevel(elementID, 0)
#3楼
如果可以创建嵌套的哈希图或数组,那么我可以简单地从表头开始将表格添加到嵌套数组中。 我必须跟踪每行到根节点,以便知道要插入嵌套数组中的哪个级别。 我可以使用记忆,这样我就不需要一遍又一遍地查找同一个父母。
编辑:我将首先将整个表读入数组,因此不会重复查询数据库。 当然,如果您的桌子很大,这将不切实际。
构建完结构后,我必须先遍历深度并打印出HTML。
没有更好的基本方法来使用一个表存储此信息(尽管我可能错了;),并且希望看到一种更好的解决方案)。 但是,如果您创建一个方案来使用动态创建的db表,那么您将在牺牲简单性和SQL地狱的风险的同时开辟了一个全新的世界;)。
#4楼
您可以使用哈希图模拟任何其他数据结构,因此这不是一个可怕的限制。 从顶部到底部扫描,您将为数据库的每一行创建一个哈希图,并为每一列创建一个条目。 将每个哈希表添加到键入ID的“主”哈希表。 如果任何节点具有尚未出现的“父”节点,请在主哈希图中为其创建一个占位符条目,并在看到实际节点时将其填充。
要打印出来,请对数据进行简单的深度优先传递,并始终跟踪缩进级别。 通过为每一行保留一个“子项”条目,并在扫描数据时填充它,可以使此过程变得更加容易。
至于是否有一种“更好”的方式将树存储在数据库中,这取决于您如何使用数据。 我已经看到了具有已知最大深度的系统,该系统为层次结构中的每个级别使用了不同的表。 如果树中的级别毕竟不完全相等(顶层类别与叶子不同),这很有道理。
#5楼
好的选择,我会使用对象。 我会为每个记录创建一个对象,其中每个对象都有一个children
对象集合,并将它们全部存储在ID为键的assoc数组(/ hashtable)中。 并快速浏览该收藏集,将子级添加到相关的子级字段中。 简单。
但是由于限制某些良好的OOP的使用对您很无聊,因此我可能会根据以下条件进行迭代:
function PrintLine(int pID, int level)
foreach record where ParentID == pID
print level*tabs + record-data
PrintLine(record.ID, level + 1)
PrintLine(0, 0)
编辑:这类似于其他几个条目,但我认为它稍微干净一些。 我要添加的一件事:这是非常消耗SQL的。 真讨厌 如果可以选择,请执行OOP路线。
来源:oschina
链接:https://my.oschina.net/u/3797416/blog/3158395