穿线树实现无限级分类

来源:互联网 发布:希尔排序算法代码 编辑:程序博客网 时间:2024/04/30 09:40

分类,分组在Web应用中到处可见,理想的原型设计,总是会遇到各种各样的新的更新需求,而来自客户的需求变更(客户简单的一句加一个下级分类),可能导致的是项目重大重构。为什么我们不从一开始就把分类做成树形,可无限扩展呢?

最简单的树形分类扩展其实很好实现,一个记录中加一个parent字段指向它的父结点就行,但这样做有很多问题实现起来很复杂,有的甚至需要用到递归来实现,对编程和数据库压力都很大,如:

1. 一个节点下有多少个子孙结点(注意是子孙)?

2. 从根到本节点的路径如何?


很幸运的是,如何你不是野路子出身,则一定学过数据结构。就应该记得在树的那一章中一个概念称做【穿线树】,它特别适应我们WEB中分类、分组这样可能出现无限层级,读取多而插入少的应用场景。结合穿线树的概念,我们改造一下,在数据训中实现无限级树形分类。

1. 树的结构

  

图中节点: 字母为节点内容,第一个数字为节点ID,第二个数字为节点父ID,通过父ID的指向,我们可以构造出无限分级的树。

2. 树的穿线

对上面已生成的树,我们给各个节点加上LFT和RGT属性,其取值规则为:

1. LFT为自己的前一个兄弟的RGT+1,如果自己是长子,则为父节点LFT+1

2. RGT为自己的最小的儿子的RGT+1,如果没有子结点,则为自己的LFT+1

穿完线后的树样子如下(节点后面新增的数字分别为LFT和RGT,原来的ID,父ID用灰色以免干扰),注意,各节点的LFT,RGT与节点的ID,父ID没有任何关系!


3. 树的访问:将上述树的各个节点保存到数据库中,之后可以:

    1) 选出所有叶节点(即没有子结点)

           select * from tree where RGT=LFT+1

    2)  获取本节点的所有子节点

           select * from tree where pid=me.id

    3)  获取本节点所有子孙节点

           select * from tree where LET>me.LFT and RGT<me.RGT

    4)  获取本节点的直系祖先(到根的路径)

           select * from tree where LFT<me.LFT and RGT>me.RGT order by LFT desc

    5)获取我的所有兄弟们(从大到小)

           select * from tree where pid=m.pid order by LFT

    6)获取我的哥哥(我前一个节点),弟弟同理

           select * from tree where pid=me.pid and GRT=me.LFT-1

4. 树的增加、删除与移动, 穿线树利于查询,无限级都可以用简单的SQL语句来实现,但为此付出的代价是新增、删除和移动节点相当麻烦。直接上数据库结构与JFinal代码如下:

    1)数据库结构

CREATE TABLE category(        id INT NOT NULL AUTO_INCREMENT,        name VARCHAR(200) NOT NULL,        father INT DEFAULT '1',        lft INT,        rgt INT,        moving INT DEFAULT '0' NOT NULL,        PRIMARY KEY (id)    )  ENGINE=MyISAM DEFAULT CHARSET=utf8
    2) 新增、删除、移动

    public void jsTreeCreate() {        int rgt = Db.queryInt("select rgt from category where id=?", getParaToInt("id"));        Db.update("update category set rgt=rgt+2 where rgt>=?", rgt);        Db.update("update category set lft=lft+2 where lft>=?", rgt);        new Category().set("father", getParaToInt("id")).set("name", getPara("text")).set("lft", rgt).set("rgt", rgt + 1).save();        final int id = Db.queryInt("select max(id) from category");        CacheKit.removeAll("Memory1000");        renderJson(new HashMap() {{            put("id", id);        }});    }    public void jsTreeDelete() {        Record record = Db.findFirst("select lft,rgt from category where id=?", getParaToInt("id"));        int lft = record.getInt("lft");        int rgt = record.getInt("rgt");        Db.update("delete from category where lft>=? and rgt<=?", lft, rgt);        Db.update("update category set lft=lft-? where lft>?", rgt - lft + 1, rgt);        Db.update("update category set rgt=rgt-? where rgt>?", rgt - lft + 1, rgt);        CacheKit.removeAll("Memory1000");        renderText("ok");    }    public void jsTreeMove() {        //参数:jsTreeMove?id=76&parent=72&position=0        // 1.标记        Record record = Db.findFirst("select lft,rgt from category where id=?", getParaToInt("id"));        int srcLeft = record.getInt("lft");        int srcRight = record.getInt("rgt");        Db.update("update category set moving=1 where lft>=? and rgt<=?", srcLeft, srcRight);        // 2.逻辑删除        Db.update("update category set lft=lft-? where lft>?", srcRight - srcLeft + 1, srcRight);        Db.update("update category set rgt=rgt-? where rgt>?", srcRight - srcLeft + 1, srcRight);        // 3.找到插入点        Category father = Category.dao.findById(getParaToInt("parent"));        int dstLeft = father.getInt("lft") + 1;        List<Record> brother = Db.find("select * from category where moving=0 and  father=? order by lft", father.getInt("id"));        if (brother.size() > 0 && getParaToInt("position") > 0) {            dstLeft = brother.get(getParaToInt("position") - 1).getInt("rgt") + 1;        }        // 3.空出位置        Db.update("update category set rgt=rgt+? where moving=0 and rgt>=?", srcRight - srcLeft + 1, dstLeft);        Db.update("update category set lft=lft+? where moving=0 and lft>=?", srcRight - srcLeft + 1, dstLeft);        // 4.逻辑插入        Db.update("update category set lft=lft+?,rgt=rgt+?,moving=0 where moving=1", dstLeft - srcLeft, dstLeft - srcLeft);        Db.update("update category set father=? where id=?", father.getInt("id"), getParaToInt("id"));        CacheKit.removeAll("Memory1000");        renderText("ok");    }
    上述代码是与jsTree进行树的展示和编辑


欢迎同行批评指正





0 0
原创粉丝点击