本文介绍的方法基于多叉树的前序遍历序列,是所有数据库树结构存储方案中查询子树速度最快的方案。最早发表在这里"http://drinkjava2.iteye.com/blog/2353983",但那篇文章太啰嗦了,这是整理后的精简版,其实原理很简单,几句话就能说完。
目前常见的树形结构数据库存储方案有以下四种,但是都存在一定问题:
1)Adjacency List(邻接表):每个节点仅记录父节点主键。优点是简单,缺点是访问子树需要递归遍历,对数据库压力大(即使是支持递归SQL的数据库)。
2)Path Enumerations( 路径枚举):用一个字符串记录当前节点所在路径。优点是查询方便,缺点是占用空间大,查询需要使用like模糊方法,效率低,插入新记录时要手工更改此节点以下所有路径,维护不便。
3)Closure Table(闭包表):专门一张表维护Path,缺点是占用空间大,操作不直观。
4)Nested Sets (嵌套集):记录左值和右值,优点是查询子树无需递归,缺点是非常复杂、难操作。
本文介绍的基于树形结构的前序遍历序列方法,示意图如下:
如上图左边的树结构,映射在数据库里的结构见右图表格,注意整个表格是一个排好序的树结构的前序遍历序列,相同节点深度的排序以line为准。 表格的最后一行(或每个根节点)必须有一个END标记,level设为0,上表中的ID为主键,Level为树节点的深度。在以上基础上,只要一句SQL,就可以无递归查询出任意节点的所有子树节点, 比某些数据自带的基于递归原理的查询更高效。 假设节点的行号为X,level为Y,则查询整个子树的SQL为:
select * from tb where line>=X and line<(select min(line) from tb where line>X and level<=Y)
例如获取D节点及其所有子节点:
select * from tb where line>=7 and line< (select min(line) from tb where line>7 and level<=2)
它依据的原理很简单:按树结构的前序遍历序列存储的树结构,判断它的所有子结点,只要从这个节点开始往下划竖线,竖线右边的全是它的子节点,直到碰到竖线上或竖线左边出现非空值则终止,原理图如下:
这种方案的优点是查询高效,删除高效(只需要删除当前行即可,虽然会造成line字段的跳号,但是不影响使用),插入节点也很简单,只需要2行简单的SQL即可,例如要在line=9和line=10的两个节点之间插入一个新节点,则新节点行号应为10,原行号为10的将变为11, SQL操作为:
update tb2 set line=line+1 where line>=10; //把10以后的所有行号加1
insert into tb (line,id,level) values (10,'T',4); //执行插入新节点操作
虽然影响的行数非常多,但是理解和维护都非常简单。 如果担心插入操作对性能的影响,还可以将行号跳号设计(如按100000跳号),新的节点可以插入在两个节点的空号区中段(如果节点间存在空号区),这样可以很大程序上消除进行加1操作的概率。另外一个表只需要一个End标记,可以设为一个固定的非常大的数值(要大于所有line值)。
这种方案的缺点是当需要移动节点时,必须维护整张表的前序遍历排序顺序,必要时要进行整张表或子树的重排序, 这是一个很耗时的工作(其它三种方案Path Enumerations/Closure Table/Nested Sets也有这个缺点),这是不可避免的,是以始终维护一个索引为代价换取查询性能的提高。因此它最适合只有增删查操作,但是很少有移动节点的场合。
总结:这种方案最适合的场合是树的深度很深(可以超过上百万),不经常移动节点、对查询速度要求极高的场合(因为无递归)。
本文实际上可以抽象成一种利用前序遍历给多叉树建查询索引的方案,即使在没有数据库存在的情况下,也是有可能在程序中应用到这种方案的,只要将SQL中的查询功能改成手工进行数组遍历即可。
另外,数据库开发者也可以考虑,对于邻接表模式(即只有id和pid两个关键字段)存储的树结构, 可以用这种方法创建一个内部索引,将level和line作为数据库表的隐藏字段,这样用户就可以不用手工维护level、line以及维护前序遍历排序这些与业务无关的操作,这才是最人性化的使用方式。
- 大小: 90.8 KB
- 大小: 11 KB
分享到:
相关推荐
在IT行业中,处理树形结构的数据是一个常见的挑战,特别是在实现无限分类的问题时。前序遍历作为一种有效的树遍历方法,被广泛应用于解决此类问题,因为它能提供更好的性能和灵活性。本文将深入探讨前序遍历树模型,...
总的来说,设计树形结构的数据库表Schema是一项挑战,需要平衡直观性、存储效率和查询性能。根据实际场景和需求,可以选择直观的`{Node_id, Parent_id}`方案,或者优化后的基于左右值编码的方法。理解并掌握这两种...
无限级分类允许我们创建一个可以无限扩展的层级结构,每个节点都可以有任意数量的子节点,这样的结构通常被称为树形结构。在这个场景中,我们将讨论如何利用二叉树的数据结构来实现无限级分类,并重点探讨一种改进的...
在这个场景中,我们关注的是如何在C#编程环境下处理父子关系树(通常称为层级数据或树形结构),并通过递归方法进行遍历。这样的操作常见于构建组织结构、文件系统或者产品结构(如物料清单BOM)。下面我们将深入...
本篇文章将深入探讨“树形结构设计”在Java环境下的实现,并结合给出的链接资源——一篇在CSDN博客上的文章(虽然无法直接访问,但我们可以根据描述推测其内容),以及名为“tms”的压缩包文件,来解析相关知识点。...
在JavaScript中,实现树形结构是一项常见的任务,特别是在前端开发中,例如构建文件系统、组织菜单、展现数据层级等。树形结构是一种数据结构,它由节点(或称为元素)组成,每个节点可以有零个或多个子节点,形成一...
二叉树是一种在计算机科学中广泛应用的数据结构,它是由节点(通常称为顶点)和连接这些节点的边构成的树形结构。每个节点最多有两个子节点,分别被称为左子节点和右子节点。二叉树的特性使得它们非常适合用于搜索、...
在计算机科学中,树形结构是一种非常重要的数据结构,它模拟了自然界中的树状关系,广泛应用于各种领域,如文件系统、数据库索引、编译器设计等。在这个主题中,我们将深入探讨一个使用JavaScript实现的简单树形结构...
2. 递归的理解和使用,这是处理树形结构的基础。 3. 动态规划或贪心策略,可能在某些特定问题的解决中发挥作用。 4. 错误处理和边界条件检查,确保程序的健壮性。 总之,这个课程设计涵盖了数据结构与算法的基础...
在IT行业中,树形结构是一种常见的数据结构,用于表示具有层次关系的数据。"Tree递归.rar"这个压缩包文件提供了一个关于如何使用递归算法处理树结构的示例。递归是编程中的一种强大工具,它允许函数或方法调用自身来...
在讨论关系数据库存储树形结构数据的理想实践之前,我们首先要明确什么是树形结构数据。树形结构数据是一种数据结构,它以树状图的形式存储数据元素之间的关系,比如文件系统的目录结构、组织架构、家族谱系等。每棵...
在IT领域,特别是软件开发中,树形结构是一种常见的数据结构,用于表示具有层次关系的数据。在 Delphi 编程环境中,处理树的遍历是一项基础且重要的技能,尤其是在设计用户界面、数据库查询优化或者算法实现时。本篇...
在IT领域,尤其是在软件开发中,树形结构是一种常见的数据表示方式,用于组织和管理数据。树的基本操作涉及创建、遍历、修改和查询树节点。在这个话题中,我们将深入探讨以下几个方面: 1. **树的基本操作**: - *...
Java树形结构是一种数据结构,它模仿了自然界中的树状模型,由节点(或称为顶点)和边组成。在计算机科学中,每个节点可以有零个或多个子节点,而根节点没有父节点。这种结构广泛应用于各种编程场景,如文件系统、...
二叉树是一种特殊的树形结构,它的每个节点最多只能有两个子节点,即左子节点和右子节点。二叉树广泛应用于计算机科学和软件工程中,如数据库索引、文件系统、编译器等。 二、、二叉树创建算法 创建二叉树可以使用...
本课程设计的重点是掌握二叉树的各种遍历方法,以及相关的操作,如计算结点数、每层结点数和打印树形结构。我们将使用C++语言进行实现,这是一种广泛应用的编程语言,特别适合处理算法和数据结构问题。 首先,我们...
1. **前序遍历**:前序遍历的基本步骤为先访问根节点,再递归地遍历左子树,最后递归地遍历右子树。这种方式下,根节点总是在其子节点之前被访问。 2. **中序遍历**:中序遍历的顺序是先递归地遍历左子树,然后...
了解并熟练掌握树的遍历和节点信息处理,对于开发涉及树形结构的Windows应用程序至关重要。无论是数据分析、文件系统浏览还是复杂菜单系统的设计,CTreeCtrl及其相关的遍历技术都扮演着核心角色。通过实践和不断学习...
在易语言中,处理数据结构如二叉树是一项常见的任务,特别是在处理递归和树形结构的数据时。二叉树遍历是理解数据结构和算法的基础,它包括前序遍历、中序遍历和后序遍历三种主要方法。 1. **二叉树的基本概念**: ...
对于树形结构,我们通常会使用"递归关联"或"闭包表"方法。闭包表是一种存储树结构关系的有效方式,它创建了一个额外的表来记录每个节点的父节点和深度信息。 在“Laravel开发-tree”项目中,我们可能看到以下关键...