层次(树型)数据的存储与访问(二)

     在《层次数据的存储与访问(一)》中,介绍了两种使用比较频繁的层次数据的存储与访问模式。这两种方法虽然可以解决大多数层次数据的处理问题,但往往不适合存取效率要求高的场合。本篇继续探讨其它两种存储访问模式:编码模式与改进的前序遍历模式。

三、编码模式:

使用特殊编码方法,将层次数据平面化到数据库表,而在节点的标识字段编码中包含父节点的层次信息,即编码模式。

编码也可以有多种选择,当然最简便的是位编码了,在下面的讨论中,先了解一下编码方法。这里使用32位整数来编码,因为整数有一个符号位,所以可用的位数只有31位,即31(3+4+4+4+4+4+4+4),这样可以表示8层,第一层用3位表示,可以表示8个节点,第二层及以下,每层用4位表示,分别可以表示16个节点。实际应用中根据需要可自由设定。

首先观察下面的一组数据。

A: 101 0000 0000 0000 0000 0000 0000 0000  (1342177280)

B: 101 0001 0000 0000 0000 0000 0000 0000  (1358954496)

C: 101 0010 0000 0000 0000 0000 0000 0000  (1375731712)

D: 101 0010 0001 0000 0000 0000 0000 0000  (1376780288)

很容易看出:

1、B、C、D都是A的子节点

2、A是B、C的父节点,C是D的父节点

3、B是A的第一个子节点,C是A的第二个子节点

4、A属于第二层,B、C属于第二层,D属于第三层

现在先给出相关编码公式,可对照研究,并理解之。

1、  编码的总位数:

N=N1+N2+N3+…+Ni 其中Ni表示第i层的位数

例:31=3+4+4+4+4+4+4+4

2、  编码方法:

节点的编码=父节点的编码+2^(N-(N1+N2+…+Ni))*j

其中i表示当前的层次,j表示当前层的第j个分类

   例:上面D的编码

   13757317112+2^(31-(3+4+4))*1=1376780288

3、  层的特征码:2^N-2^(N-(N1+N2+…+Ni))

对于任何一个节点的编码,同父节点的特征码做“与运算”,其结果为父节点的编码

   例,求B节点的父节点:

   先求B节点上层的特征码:2^31-2^(31-3)=1879048192

   B节点的编码(1358954496)101 0001 0000 0000 0000 0000 0000 0000

        与

   上层的特征码(1879048192)111 0000 0000 0000 0000 0000 0000 0000

        =                     

   父节点的编码 (1342177280)  101 0000 0000 0000 0000 0000 0000 0000

4、  节点的子节点的最大值:2^(N-(N1+N2+…+Ni))-1 同节点编码作“或运算”

例,求D节点下子节点的最大值

1376780288 | 2^(31-(3+4+4))-1=1377828863

现在了解了编码方法,那么回到上篇中提出的问题,形成的数据库表如下图所示。(table1)

 

Id

 

Title

 

Layer

 

001 0000 0000 0000 0000 0000 0000 0000 -268435456

 

电器

 

1

 

010 0000 0000 0000 0000 0000 0000 0000 -536870912

 

服装

 

1

 

001 0001 0000 0000 0000 0000 0000 0000 -285212672

 

电视

 

2

 

001 0010 0000 0000 0000 0000 0000 0000 -301989888

 

冰箱

 

2

 

001 0011 0000 0000 0000 0000 0000 0000 -318767104

 

空调

 

2

 

010 0001 0000 0000 0000 0000 0000 0000 -553648128

 

成人

 

2

 

010 0010 0000 0000 0000 0000 0000 0000 -570425344

 

儿童

 

2

 

001 0011 0001 0000 0000 0000 0000 0000 -319815680

 

柜式

 

3

 

001 0011 0010 0000 0000 0000 0000 0000 -320864256

 

窗式

 

3

 

010 0001 0001 0000 0000 0000 0000 0000 -554696704

 

上衣

 

3

在表中ID字段实际是整型数据,表中Layer字段也不是必须的,增加可方便有些查询。数据操作举例:

1、  在“成人”节点下,增加新的子节点“长裤”:

Insert into table1(Id,Title,Layer) values(2^(31-(3+4+4))*2+553648128,”长裤”,3)

2、  删除“服装”节点:

A、 求“服装”节点的下级子节点最大值:

(2^(31-3)-1)|  36870912=805306367

B、 删除“服装”节点及其下子节点:

Delete from table1 where  d>=536870912  And Id<=805306367

3、  获得“柜式”节点的路径:

第一层特征码:2^31-2^(31-3)=1879048192

第二层特征码:2^31-2^(31-(3+4))=2130706432

第三层特征码:2^31-2^(31-(3+4+4))=2146435072

Select Id & 1879048192,Id & 2130706432,Id & 2146435072 from table1 where Id=319815680

4、  求“电器”节点的所有子节点:

Select * from table1 where Id>268435456 and Id<=(268435456 | (2^(31-3))-1)

遍历及打印树,上篇已给了几个例子,就不写了。

5、  求“电器”节点下的直接子节点:

Select * from table1 where Id>268435456 and Id<=(268435456 | (2^(31-3))-1) and Layer=2

   使用编码模式,只要理解了编码的方法以后,也不难理解,其查询算法丰富,在数据库操作中不需要使用“递归”,效率很高。其次缺点是,由于层次的限制,无法做到无限级分类。

四、改进的前序遍历模式:

   所谓改进的前序遍历模式,是按照前序遍历的次序,沿着层次树的边沿遍历整个树,给每个节点按次序赋予左值和右值,将节点关联起来,从而把树平面化。

   请仔细观察是如何遍历树的以及怎样各节点编号的。

 

注意箭头的顺序,是沿着树的边缘漫游的。上面得到的数字称为节点的左值与右值,这些数值表达了节点的顺序关系,通过这些值,可以观察得出面的一些规律:

1、  子节点的左值总是大于父节点的左值,而小于父节点的右值。

如图中,“空调”(5-10)的左值都大于5并且小于10.

2、  子节点的右值总是小于父节点的右值。

3、  节点的子节点总数=(节点的右值-节点的左值-1)/2

如“服装”节点的子节点个数为(21-14-1)/2=3

4、  节点的祖先(包括父节点)的左值都小于子节点,右值都大于子节点右值

如“冰箱”(11-12),其祖先“电器”(2-13),“商品”(1-22)。

  以上规律都可用于数据库表的操作,图中的节点存储到数据库表中如下:

 

Title

 

Lft

 

Rgt

 

商品

 

1

 

22

 

电器

 

2

 

13

 

服装

 

14

 

21

 

电视

 

3

 

4

 

空调

 

5

 

10

 

冰箱

 

11

 

12

 

成人

 

15

 

18

 

儿童

 

19

 

20

 

柜式

 

6

 

7

 

窗式

 

8

 

9

 

上衣

 

16

 

17

实际应用时,表中应增加一个标识字段,另外增加一个表示层数的字段亦可。

数据操作举例:

1、  获取“成人”节点的路径:

 Select Title from table1 where Lft<15 And Rgt>18

2、  获取“商品”下所有子节点:

Select Title from table1 where lft Between 1 and 22 order by Lft Asc

遍历并打印略

3、  在“电视”节点下,添加一个子节点“液晶”:

在“电视”(3-4)下增加子节点,意味着先要腾出一个空间放置新节点,“电视”节点的右值要加2,“电视”节点的右边的所有节点的左值与右值都需要加2,也就是说所有左值和右值大于3的节点都应加上2

A, 腾空间:

Update table1 set Lft=Lft+2 where Lft>3

Update table1 set Rgt=Rgt+2 where Rgt>3

   B,插入新节点:

       Insert into table1(Title,Lft,Rgt) values(“液晶4,5)

4、  删除“空调”节点:

删除操作先删除节点及其子节点,然后节点右边的节点的,左值与右值,都得减去删除节点个数的2倍。

A、 删除:

Delete from table1 where Lft>=5 and  Rgt<10

B、 清空间:

Update table1 set Lft=Lft-(10-5+1) where Lft>9

Update table1 set Rgt=Rgt-(10-5+1) where Lft>9

   当熟悉了节点的左值与右值以后,改进的前序遍历方法很好理解,可以看到查询操作的效率非常高,也能很容易的实现无限级分类,个人认为此乃一个不错的层次数据存取解决办法,自己在项目中也多采用此方法,不过,在插入新数据时,表数据需要做一次更新,如果数据量大,插入效率就比较低了,所以需要频率插入和删除的应用,不建议使用。

分两次《层次数据的存储与访问》算是告一段落了,文中所述的四种方法,各有其使用场合,并没有完美的解决方案,总的来看后两种模式要好一些。具体使用哪一种,就看自己的需要了。

另外,近来没时间,文中大部分代码,只是举例,并未调试,以能看懂为目的。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值