采用左右值编码实现无限分级树形结构(转)

无限分级树形结构是在系统开发中很常见的,如下图

在之前实现这样的菜单一直是使用传统的方法,看数据表结构就一目了然

parent_id记录其直接父节点,组合树形结构的关键字段;parent_list记录其所有父节点,便于查询某个节点下所有子节点(一般使用MySQL的FIND_IN_SET函数),相对冗余。
对于这种结构生成树形的关键算法:根据parent_id组合一个父子(直接关系)节点映射表,即 2 => array(3, 4), 3 => array(5),然后递归优先遍历每个节点的子节点。
如果还需要按顺序排列,比如移动某个节点,则需要再添加一个字段来记录顺序。
优点:简单清晰,通俗易懂,添删改节点都很容易实现
缺点:查询效率不高(但一般树形结构都比较小)

再看另一种实现方式-左右值编码
其数据结构比较简单,但左右值需要经过遍历计算得到,很难看出直接父子关系的节点

通过图了解左右值如何定义,类似前序遍历,父节点先访问,再访问子节点,递增标记,每个节点访问两次,分左右值。

所有子节点的左右值一定在父节点的左右值范围,所以通过语句left > $parent_left AND right < $parent_right就可以查询某个节点下所有的子节点,比FIND_IN_SET优雅。而查找直属的子节点只需要加上layer = $parent_layer + 1限制。

生成树操作
只需要按left字段从小到大顺序输出就行,缩进层次的就根据layer值,简单。

新增操作
比如在服务端类别添加一个子节点Python,一个节点占用2个值,新增的节点就是左右值就是父节点的右值和右值+1,而影响到的节点值(蓝色)都是大于等于父节点的右值(即>=10),都是原先的值加2。
新增前

新增后

删除操作
删除可参考新增操作,只是将>=父节点右值的所有节点值减去2

移动操作
主要是改变父节点和同层节点调换操作
移动操作基本基于一个公式:任何树所占的数字数目 = 根的右值 – 根的左值 + 1。

1.改变父节点
先看下移动PHP到客户端,即PHP的父节点换成客户端,这种情况下树节点上的变化
改变前

改变后

以PHP为根的树(虽然只有一个节点)的节点值变化是以新父节点的原右值为依据,如上图,新父节点的原右值为6,那PHP新的左值就是6,这样重新遍历PHP为根的树,就相当于这棵子树上的每个节点更新为:原值 – (根的原左值 – 根的新左值),所以PHP的右值就等于7 = 9 – (8 – 6)。
而除了PHP为根的树,其他节点更新的有一定范围,范围就是新父节点的右值和PHP原先的左值(或旧父节点的左值),因为PHP是向前移,范围内的节点值是往后移,需要加上PHP树的所占的数字数目,见上面公式,这里可以总结一个定律:
新父节点_right <= ([left, right] + (移动_right – 移动_left + 1)) < 移动_left

再看下PHP向后移的情况,与前移类似,也是更新一定范围的节点,变成减定律:
移动_right < ([left, right] – (移动_right – 移动_left + 1)) < 新父节点_left

2.同层节点调换
这里同层节点调换是指兄弟节点顺序调换,为了简化,一般是移到某个兄弟节点(参考点)后面,特殊情况是移到最前,这种情况参考点就是父节点。

如图,把语言一类移到兄弟节点-数据库后面。参考点是数据库,影响范围是语言的right与数据库的right之间的值,这类移动属于后移,根据上面提到的后移减定律,可得到公式:
移动_right < ([left, right] – (移动_right – 移动_left + 1)) <= 参考点_right
而前移公式:参考点_right < ([left, right] + (移动_right – 移动_left + 1)) < 移动_left
如果是移到最前,只是前移公式的范围的左边界换为父节点_left,即公式为:父节点_left < ([left, right] + (移动_right – 移动_left + 1)) < 移动_left。
对于是前移还是后移,可以根据移动节点的left值与参考点的right值的大小关系判断。

至此,关于左右值编码实现树形结构的关键操作都已说明,主要也是公式,范围是哪些,是加还是减,我也是纠结了几天,所以上面显得有点啰嗦,如果有兴趣的还是亲手推断,也许会发现比上面更简单的更新操作。

时间: 2024-09-28 16:07:17

采用左右值编码实现无限分级树形结构(转)的相关文章

采用左右值编码来存储无限分级树形结构

参考文档: https://blog.csdn.net/comiunknown/article/details/1586020 python实现: from app.cmdb.config import dbconfig, cmdbsql, modelconfig, balantsql from app.cmdb.utils.dbutil import MysqlConnect balant_connect = MysqlConnect(db_params=dbconfig.balant_db_

YbSoftwareFactory 代码生成插件【十八】:树形结构下的查询排序的数据库设计

树形结构的排序在中国特色下十分普遍也非常重要,例如常说的五大班子,党委>人大>政府>政协>纪委,每个班子下还有部门,岗位,人员,最终排列的顺序通常需要按权力大小.重要性等进行排列,顺序排列不好可是重大的罪过,领导很生气,后果很严重.这种排序方式本质上就是典型的树形结构深度排序,但在数据库中很难直接通过SQL语句简单高效地进行处理,更不用说还要支持不同类型数据库了. 当前解决此类问题,主要有两种方法. 1. 排序码方式 原理:在每个树形节点上均设置一个排序码,排序码通常是一个字符串并

树形结构的数据库表Schema设计-基于左右值编码

树形结构的数据库表Schema设计 程序设计过程中,我们常常用树形结构来表征某些数据的关联关系,如企业上下级部门.栏目结构.商品分类等等,通常而言,这些树状结构需要借助于数据库完 成持久化.然而目前的各种基于关系的数据库,都是以二维表的形式记录存储数据信息,因此是不能直接将Tree存入DBMS,设计合适的Schema及其对 应的CRUD算法是实现关系型数据库中存储树形结构的关键. 理想中树形结构应该具备如下特征:数据存储冗余度小.直观性强:检索遍历过程简单高效:节点增删改查CRUD操作高效.无意

【OC学习-31】利用KVC即键值编码来访问一些对象的属性,尽管这些属性是私有的

最简单的想法是我们创建一个对象,然后利用某些方法给这个对象赋值,然后再用某些方法调用这个值.当然这种描述很类似于字典的键值概念,确实今天学习的就是这样一种方法:KVC,Key-Value coding键值编码. (1)简单的KVC 创建一个Book类,在Book.h里写一个私有的实例变量name,然后再main.m里创建一个book1对象,再给这个book1对象的私有变量name赋值,然后再调用这个值.神奇的地方就在这里:这种方法根本不管这个实力变量是private还是public.呃,Book

IOS开发之旅-KVC【键值编码】

在日常开发中,读取修改对象的属性值时,通常是点调用对应的属性进行相关操作.另外一种方式是通过键值编码,简称KVC,在键值编码中主要使用以下方法 /* Given a key that identifies an attribute or to-one relationship, return the attribute value or the related object. Given a key that identifies a to-many relationship, return a

KVC-键值编码是一种间接地访问实例变量的方式

KVC 键值编码是一种间接的访问实例变量的方式[对象 setValue:@"A" forKey:@"name"]; 工作原理:    1.先查找该类有没有对应的setName: setter方法.如果有则调用setter方法给实例变量赋值,如果没有,转到第二步:    2.在查找带下划线的实例变量_name.如果有则给_name赋值,如果没有则转到第三步    3.最后查找和key值相同的实例变量name,如果有,则给name赋值,如果没有,转到第四步    4.调

无限树形结构的数据库表设计

前言: 无限树形结构的数据库表设计的是否合理,直接影响到UI层是否方便根据树来查询关联的数据. 1.表字段: F_BtEd2kTypeId int Unchecked F_Name nvarchar(50) Checked F_ParentTypeId nvarchar(50) Checked F_Code nvarchar(50) Checked F_RecordStatus int Checked 2.表数据: 3.说明: 如2所示, 1)如果上表的数据关联上了一张表A,通过BtEd2kTy

OC8-属性 KVC是键值编码

1.属性,是oc提供的一种快速的模式化的创建实例变量的方式. (1)属性是通过@property标记的, (2)属性会在背后,默默的帮我们做set和get方法 2.属性做的工作 (1)创建一个实例变量,名字是下划线加属性名, (2)帮我们自动get和setter 的方式,创建一组方法, 3.点语法,点语法是专门为了setter 和getter 方法配备的一种语法糖.会自动根据语法和语境调用是哪一种方法, (1) (.)其实就是转换成了getter 和setter 方法,p.hobby.lengt

Objective-C(十七、KVC键值编码及实例说明)——iOS开发基础

结合之前的学习笔记以及参考<Objective-C编程全解(第三版)>,对Objective-C知识点进行梳理总结.知识点一直在变,只是作为参考,以苹果官方文档为准~ 十七.键值编码 KVC 关于KVC的知识点将通过下列例子来展开说明: Person.h文件,Person类拥有name和age两个成员变量 @interface Person : NSObject { @private NSString *_name; NSInteger _age; } - (void)setAge:(NSIn