带有层号的先根遍历树存储基于plsql的代码实现

来源:互联网 发布:淘宝代运营团队 编辑:程序博客网 时间:2024/06/05 23:30

本文来自李明子csdn博客(http://blog.csdn.net/free1985),商业转载请联系博主获得授权,非商业转载请注明出处!
摘要:本文介绍了带有层号的先根遍历树存储基于plsql的代码实现,本文中涉及的代码编写于2014年3月。另外,本文提供了测试表创建语句以及插入节点、获取直接子节点、获取自身及子孙节点、获取根到指定节点的路径、删除节点等方法的存储过程源码与测试样例。

1 存储方式介绍

带有层号的先根遍历树存储方式的主要思想是通过记录先根遍历中第一次访问节点时的次序号(以下称左值)与回溯时第二次访问的次序号(以下称右值)来维护树型结构的层次关系。由先根遍历的概念可知,子节点的左值必然大于父节点左值,而右值必然小于父节点的右值。再结合排序操作就可以很容易的在不递归的情况下对树型数据进行各种查询操作。另外,在记录左值、右值的基础上,该方式还维护了节点的层号,以降低查询直接(父)子节点的时间复杂度。
带有层号的先根遍历树存储结构如表1-1所示。

表1-1 带有层号的先根遍历树的存储结构
表1-1  带有层号的先根遍历树的存储结构

2 建表语句

带有层号的先根遍历树在Oracle下的建表语句如下。

CREATE TABLE TREE(    NODENAME   NVARCHAR2(50) NOT NULL,     LEVELNUM  NUMBER(8) NOT NULL,     LEFTVALUE   NUMBER(8) NOT NULL,     RIGHTVALUE  NUMBER(8) NOT NULL );COMMENT ON TABLE TREE IS '带有层号的先根遍历树';COMMENT ON COLUMN TREE.NODENAME   IS '节点名';COMMENT ON COLUMN TREE.LEVELNUM  IS '层号,从1开始';COMMENT ON COLUMN TREE.LEFTVALUE   IS '先根遍历左值';COMMENT ON COLUMN TREE.RIGHTVALUE  IS '先根遍历右值';ALTER TABLE TREE ADD CONSTRAINT PK_NODENAME    PRIMARY KEY (NODENAME);

3 插入节点

3.1 实现
插入节点存储过程代码如下:

procedure InsertNode --插入节点  (parentName in nvarchar2, --父节点名,插入根时为空   nodeName  in nvarchar2 --节点名   ) is    parentRight TREE.RIGHTVALUE%type; --父节点右值    parentLevel TREE.LEVELNUM%type; --父节点层次  begin    if parentName is null then      insert into TREE values (nodeName, 1, 1, 2);    else      --获取父节点右值      select RIGHTVALUE, LEVELNUM        into parentRight, parentLevel        from TREE       where NODENAME = parentName;      update TREE         set LEFTVALUE = LEFTVALUE + 2       where LEFTVALUE > parentRight - 1;      update TREE         set RIGHTVALUE = RIGHTVALUE + 2       where RIGHTVALUE > parentRight - 1;      insert into TREE      values        (nodeName, parentLevel + 1, parentRight, parentRight + 1);    end if;  end InsertNode;

3.2 测试
比如,我们要创建如图3-1所示的树。
图3-1  测试用例树结构图
图3-1 测试用例树结构图

要创建上面的树,可以调用以下示例代码:

begin  -- Call the procedure  tree_package.InsertNode('', 'A');  tree_package.InsertNode('A', 'B');  tree_package.InsertNode('A', 'C');  tree_package.InsertNode('A', 'D');  tree_package.InsertNode('C', 'E');  tree_package.InsertNode('C', 'F');  tree_package.InsertNode('C', 'G');  end;  

执行后测试代码后,表记录被更新为如表3-1所示的状态。

表3-1 插入节点后的表记录
表3-1  插入节点后的表记录*

4 获取直接子节点

4.1 实现
获取直接子节点存储过程代码如下:

procedure GetChildren --获得子节点  (parentName IN NVARCHAR2, --父节点名   children  OUT RETCURSOR --子节点名   ) is    parentLeft  TREE.LEFTVALUE%type; --父节点左值    parentRight TREE.RIGHTVALUE%type; --父节点右值    parentLevel TREE.LEVELNUM%type;  begin    select LEFTVALUE, RIGHTVALUE, LEVELNUM      into parentLeft, parentRight, parentLevel      from TREE     where NODENAME = parentName;    open children for      select NODENAME        from TREE       where LEFTVALUE between parentLeft and parentRight         and LEVELNUM = parentLevel + 1       order by LEFTVALUE asc;  end GetChildren;

4.2 测试
比如我们要获取节点A的直接子节点,调用GetChildren 后,得到的游标如表4-1所示。

表4-1 获取直接子节点返回结果游标
表4-1  获取直接子节点返回结果游标

5 获取当前节点及子孙节点

5.1 实现
获取当前节点及子孙节点存储过程代码如下:

  procedure GetDescendants --获取子孙节点  (parentName IN NVARCHAR2, --父节点名   descendants  OUT RETCURSOR --子孙节点名   )is    parentLeft  TREE.LEFTVALUE%type; --父节点左值    parentRight TREE.RIGHTVALUE%type; --父节点右值  begin    select LEFTVALUE, RIGHTVALUE      into parentLeft, parentRight      from TREE     where NODENAME = parentName;    open descendants for      select NODENAME        from TREE       where LEFTVALUE between parentLeft and parentRight       order by LEFTVALUE asc;  end GetDescendants;

5.2 测试
比如我们要获取节点A及其子孙节点,调用GetDescendants后,得到的游标如表5-1所示。

表5-1 获取当前节点及其子孙节点返回结果游标
表5-1  获取当前节点及其子孙节点返回结果游标

6 获取根到指定节点的路径

6.1 实现
获取根到指定节点的路径存储过程代码如下:

 procedure GetNodePath --获得节点路径  (node IN NVARCHAR2, --节点名   nodePath OUT RETCURSOR --节点路径   ) is    nodeLeft  TREE.LEFTVALUE%type; --节点左值    nodeRight TREE.RIGHTVALUE%type; --节点右值  begin    select LEFTVALUE, RIGHTVALUE      into nodeLeft, nodeRight      from TREE     where NODENAME = node;    open nodePath for      select NODENAME        from TREE       where LEFTVALUE <= nodeLeft         and RIGHTVALUE >= nodeRight       order by LEFTVALUE asc;  end GetNodePath;

6.2 测试
比如我们要获取根到节点G的路径,调用GetNodePath后,得到的游标如表6-1所示。

表6-1 根到指定节点返回结果游标
表6-1  根到指定节点返回结果游标

7 删除节点

7.1 实现
删除节点的存储过程代码如下:

procedure DeleteNode --删除节点及其子节点  (node IN NVARCHAR2 --节点名   ) is    nodeLeft     TREE.LEFTVALUE%type; --节点左值    nodeRight    TREE.RIGHTVALUE%type; --节点右值    delNodeCount NUMBER; --删除的节点数  begin    select LEFTVALUE, RIGHTVALUE      into nodeLeft, nodeRight      from TREE     where NODENAME = node;    delNodeCount := (nodeRight - nodeLeft+1)/2;    delete from TREE     where LEFTVALUE >= nodeLeft       and RIGHTVALUE <= nodeRight;    update TREE       set LEFTVALUE = LEFTVALUE - delNodeCount * 2     where LEFTVALUE > nodeLeft;    update TREE       set RIGHTVALUE = RIGHTVALUE - delNodeCount * 2     where RIGHTVALUE > nodeRight;  end DeleteNode;

7.2 测试
比如,我们要删除节点C,则C的子节点E、F、G将一并删除。删除后的表记录状态如表7-1所示。

表7-1 删除节点后的表记录
表7-1  删除节点后的表记录

阅读全文
1 0