穿线树实现无限级分类
来源:互联网 发布:希尔排序算法代码 编辑:程序博客网 时间: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=utf82) 新增、删除、移动
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进行树的展示和编辑
欢迎同行批评指正
- 穿线树实现无限级分类
- (C#)实现无限级分类树
- 无限级分类的实现
- 无限级分类的实现
- DropDownList实现无限级分类
- DropDownList实现无限级分类
- 无限级分类的实现
- ASP实现无限级分类
- 无限级分类的实现
- 无限级分类实现思路
- 无限级分类实现思路
- 无限级分类实现思路
- 商品分类实现无限级
- 用迭代实现无限级分类
- php实现无限级分类
- PHP实现无限级分类
- php实现无限级分类
- 二叉树的递归穿线实现
- MySQL和Oracle的主要差别
- Swift之UIDatePicker
- stunnel点滴
- [IOS开发记录]ios10下使用Xcode8.2获取gps位置信息(swift3.0)
- JNI基础(八)开辟C进程
- 穿线树实现无限级分类
- ifdefine/define/endif
- androidSDK配置环境变量
- CSS3--过渡
- SSH用户权限管理(三)
- HDU 2795 Billboard【线段树好题,单点更新】
- 动态链接库dll的 静态加载 与 动态加载
- 【Unity】获得当前脚本的物体的transform和gameobject
- C风格字符串