专注于Jsp开发,为Jsp开发提供源动力 VM主机| 海外空间| 郑州网站建设| 郑州网络公司| 洛阳网站建设
jsp空间

java树形结构 算法

添加时间:[2010-3-8 8:35:51] 

最近看到一个有意思的树形结构,为每个节点添加了lftrgt两个属性。这样查找该节点的子节点、查找该节点所有父节点,就不用去递归查询,只需要用betweenand语句就可以实现。下面以创建一个栏目树为例,以下是我的理解。

  一般来讲,我们创建栏目树的时候,大多只需要一个外键parentid来区分该节点属于哪个父节点。数据库的设计如下图:
这样一来,

1.查找该节点的所有子节点,则需要采用sql的递归语句:

 

Sql代码
  • select * from tableName connect by prior id=sj_parent_id start with  id=1  
  • select*fromtableNameconnectbypriorid=sj_parent_idstartwithid=1

     

     (oracle 写法,mysql目前不支持,如果mysql想查找树形,可以利用存储过程).

    2.查找该节点的父节点的sql递归语句:

     

    Sql代码
  • select * from tableName connect by prior sj_parent_id =id start with  id=1  
  • select*fromtableNameconnectbypriorsj_parent_id=idstartwithid=1 

     如果数据量过大或者层次太多,那么这样操作是会影响性能的。

     

      “任何树形结构都可以用二叉树来表示”。其实我们创建的栏目树就是一个简型的二叉树。根据数据结构里面二叉树的遍历,再稍微修改下,将数据库设计如下图所示:

     


    这样我们查找该节点的所有子节点,则只需要查找idlftrgt之间的所有节点即可。

    1.查找该节点的所有子节点的Sql语句为:

    <!--EndFragment-->

     

    <!--EndFragment-->

    Sql代码
  • select * from tb_subject s,tb_subject t where s.lft between t.lft and t.rgt and t.id=1  
  • select*fromtb_subjects,tb_subjecttwheres.lftbetweent.lftandt.rgtandt.id=1 

    2.查找该节点的所有父节点的sql语句为:

    <!--EndFragment-->

    Sql代码
  • select s.* from tb_subject s,tb_subject t where s.lft<t.lft and (s.rgt-s.lft)>1 and s.rgt>t.rgt and t.id=1  
  • selects.*fromtb_subjects,tb_subjecttwheres.lft<t.lftand(s.rgt-s.lft)>1ands.rgt>t.rgtandt.id=1 

     下面来详细讲解下,怎么用java来实现这种算法。

    <!--EndFragment--> 1. 新增节点

     新增节点比较简单,基本步骤为

     A. 查找当前插入节点的父节点的lft

     B. 将树形中所有lftrgt节点大于父节点左值的节点都+2

     C. 将父节点左值+1,左值+2分别作为当前节点的lftrgt

     因为项目中采用的是struts2+hibernate3.2+spring2.5的框架,代码如下:

    <!--EndFragment-->

    Java代码
  • public boolean onSave(Object entity, Serializable id, Object[] state,  
  •             String[] propertyNames, Type[] types) {  
  •         if (entity instanceof HibernateTree) {  
  •             HibernateTree tree = (HibernateTree) entity;  
  •             Long parentId = tree.getParentId();  
  •             String beanName = tree.getClass().getName();  
  •             Session session = getSession();  
  •             FlushMode model = session.getFlushMode();  
  •             session.setFlushMode(FlushMode.MANUAL);  
  •             Integer myPosition = new Integer(0);  
  •             //查找父节点的左值  
  •             if (parentId != null) {  
  •                 String hql = "select b.lft from " + beanName  
  •                         + " b where b.id=:pid";  
  •                 myPosition = (Integer) session.createQuery(hql).setLong("pid",  
  •                         parentId).uniqueResult();  
  •             }  
  •             //将树形结构中所有大于父节点左值的右节点+2  
  •             String hql1 = "update " + beanName  
  •                     + " b set b.rgt = b.rgt + 2 WHERE b.rgt > :myPosition";  
  •             //将树形结构中所有大于父节点左值的左节点+2  
  •             String hql2 = "update " + beanName  
  •                     + " b set b.lft = b.lft + 2 WHERE b.lft > :myPosition";  
  •             if (!StringUtils.isBlank(tree.getTreeCondition())) {  
  •                 hql1 += " and (" + tree.getTreeCondition() + ")";  
  •                 hql2 += " and (" + tree.getTreeCondition() + ")";  
  •             }  
  •             session.createQuery(hql1).setInteger("myPosition", myPosition)  
  •                     .executeUpdate();  
  •             session.createQuery(hql2).setInteger("myPosition", myPosition)  
  •                     .executeUpdate();  
  •             session.setFlushMode(model);  
  •             //定位自己的左值(父节点左值+1)和右值(父节点左值+2)  
  •             for (int i = 0; i < propertyNames.length; i++) {  
  •                 if (propertyNames[i].equals(HibernateTree.LFT)) {  
  •                     state[i] = myPosition + 1;  
  •                 }  
  •                 if (propertyNames[i].equals(HibernateTree.RGT)) {  
  •                     state[i] = myPosition + 2;  
  •                 }  
  •   
  •             }  
  •             return true;  
  •         }  
  •         return false;  
  •     }  
  • publicbooleanonSave(Objectentity,Serializableid,Object[]state, String[]propertyNames,Type[]types){ if(entityinstanceofHibernateTree){ HibernateTreetree=(HibernateTree)entity; LongparentId=tree.getParentId(); StringbeanName=tree.getClass().getName(); Sessionsession=getSession(); FlushModemodel=session.getFlushMode(); session.setFlushMode(FlushMode.MANUAL); IntegermyPosition=newInteger(0); //查找父节点的左值 if(parentId!=null){ Stringhql="selectb.lftfrom"+beanName +"bwhereb.id=:pid"; myPosition=(Integer)session.createQuery(hql).setLong("pid", parentId).uniqueResult(); } //将树形结构中所有大于父节点左值的右节点+2 Stringhql1="update"+beanName +"bsetb.rgt=b.rgt+2WHEREb.rgt>:myPosition"; //将树形结构中所有大于父节点左值的左节点+2 Stringhql2="update"+beanName +"bsetb.lft=b.lft+2WHEREb.lft>:myPosition"; if(!StringUtils.isBlank(tree.getTreeCondition())){ hql1+="and("+tree.getTreeCondition()+")"; hql2+="and("+tree.getTreeCondition()+")"; } session.createQuery(hql1).setInteger("myPosition",myPosition) .executeUpdate(); session.createQuery(hql2).setInteger("myPosition",myPosition) .executeUpdate(); session.setFlushMode(model); //定位自己的左值(父节点左值+1)和右值(父节点左值+2) for(inti=0;i<propertyNames.length;i++){ if(propertyNames[i].equals(HibernateTree.LFT)){ state[i]=myPosition+1; } if(propertyNames[i].equals(HibernateTree.RGT)){ state[i]=myPosition+2; } } returntrue; } returnfalse; } 

     2. 修改节点

      修改的时候比较麻烦,具体步骤为:

      在修改lftrgt之前,当前节点的父节点id已经改变

    a. 查出当前节点的左右节点(nodelftnodergt),并nodergt-nodelft+1 = span,获取父节点的左节点parentlft

    b. 将所有大于parentlftlft(左节点)rgt(右节点)的值+span

    c. 查找当前节点的左右节点(nodelftnodergt),并parentlft-nodelft+1 = offset

    d. 将所有lft(左节点) between nodelft and nodergt的值+offset

    e. 将所有大于nodergtlft(左节点)rgt(右节点)的值-span

     Java代码如下:

    <!--EndFragment-->

    Java代码
  • public void updateParent(HibernateTree tree, HibernateTree preParent,  
  •             HibernateTree curParent) {  
  •         if (preParent != null && preParent != null  
  •                 && !preParent.equals(curParent)) {  
  •             String beanName = tree.getClass().getName();  
  •             // 获得节点位置  
  •             String hql = "select b.lft,b.rgt from " + beanName  
  •                     + " b where b.id=:id";  
  •             Object[] position = (Object[]) super.createQuery(hql).setLong(  
  •                     "id", tree.getId()).uniqueResult();  
  •             System.out.println(hql+"| id = "+tree.getId());   
  •             int nodeLft = ((Number) position[0]).intValue();  
  •             int nodeRgt = ((Number) position[1]).intValue();  
  •             int span = nodeRgt - nodeLft + 1;  
  •             // 获得当前父节点左位置  
  •             hql = "select b.lft from " + beanName + " b where b.id=:id";  
  •             int parentLft = ((Number) super.createQuery(hql).setLong("id",  
  •                     curParent.getId()).uniqueResult()).intValue();  
  •   
  •             System.out.println(hql+"| id = "+curParent.getId());  
  •             // 先空出位置  
  •             String hql1 = "update " + beanName + " b set b.rgt = b.rgt + "  
  •                     + span + " WHERE b.rgt > :parentLft";  
  •             String hql2 = "update " + beanName + " b set b.lft = b.lft + "  
  •                     + span + " WHERE b.lft > :parentLft";  
  •             if (!StringUtils.isBlank(tree.getTreeCondition())) {  
  •                 hql1 += " and (" + tree.getTreeCondition() + ")";  
  •                 hql2 += " and (" + tree.getTreeCondition() + ")";  
  •             }  
  •             super.createQuery(hql1).setInteger("parentLft", parentLft)  
  •                     .executeUpdate();  
  •             super.createQuery(hql2).setInteger("parentLft", parentLft)  
  •                     .executeUpdate();  
  •   
  •             System.out.println(hql1+"| parentLft = "+parentLft);  
  •             System.out.println(hql2+"| parentLft = "+parentLft);  
  •               
  •             // 再调整自己  
  •             hql = "select b.lft,b.rgt from " + beanName + " b where b.id=:id";  
  •             position = (Object[]) super.createQuery(hql).setLong("id",  
  •                     tree.getId()).uniqueResult();  
  •             System.out.println(hql+"| id = "+tree.getId());  
  •             nodeLft = ((Number) position[0]).intValue();  
  •             nodeRgt = ((Number) position[1]).intValue();  
  •             int offset = parentLft - nodeLft + 1;  
  •             hql = "update "  
  •                     + beanName  
  •                     + " b set b.lft=b.lft+:offset, b.rgt=b.rgt+:offset WHERE b.lft between :nodeLft and :nodeRgt";  
  •             if (!StringUtils.isBlank(tree.getTreeCondition())) {  
  •                 hql += " and (" + tree.getTreeCondition() + ")";  
  •             }  
  •             super.createQuery(hql).setParameter("offset", offset)  
  •                     .setParameter("nodeLft", nodeLft).setParameter("nodeRgt",  
  •                             nodeRgt).executeUpdate();  
  •             System.out.println(hql+"| offset = "+offset+" | nodelft = "+nodeLft+" | nodergt = "+ nodeRgt);  
  •             // 最后删除(清空位置)  
  •             hql1 = "update " + beanName + " b set b.rgt = b.rgt - " + span  
  •                     + " WHERE b.rgt > :nodeRgt";  
  •             hql2 = "update " + beanName + " b set b.lft = b.lft - " + span  
  •                     + " WHERE b.lft > :nodeRgt";  
  •             if (tree.getTreeCondition() != null) {  
  •                 hql1 += " and (" + tree.getTreeCondition() + ")";  
  •                 hql2 += " and (" + tree.getTreeCondition() + ")";  
  •             }  
  •             super.createQuery(hql1).setParameter("nodeRgt", nodeRgt)  
  •                     .executeUpdate();  
  •             super.createQuery(hql2).setParameter("nodeRgt", nodeRgt)  
  •                     .executeUpdate();  
  •             System.out.println(hql1+"| nodeRgt = "+nodeRgt);  
  •             System.out.println(hql2+"| nodeRgt = "+nodeRgt);  
  •               
  •         }  
  •     }  
  • publicvoidupdateParent(HibernateTreetree,HibernateTreepreParent, HibernateTreecurParent){ if(preParent!=null&&preParent!=null &&!preParent.equals(curParent)){ StringbeanName=tree.getClass().getName(); //获得节点位置 Stringhql="selectb.lft,b.rgtfrom"+beanName +"bwhereb.id=:id"; Object[]position=(Object[])super.createQuery(hql).setLong( "id",tree.getId()).uniqueResult(); System.out.println(hql+"|id="+tree.getId()); intnodeLft=((Number)position[0]).intValue(); intnodeRgt=((Number)position[1]).intValue(); intspan=nodeRgt-nodeLft+1; //获得当前父节点左位置 hql="selectb.lftfrom"+beanName+"bwhereb.id=:id"; intparentLft=((Number)super.createQuery(hql).setLong("id", curParent.getId()).uniqueResult()).intValue(); System.out.println(hql+"|id="+curParent.getId()); //先空出位置 Stringhql1="update"+beanName+"bsetb.rgt=b.rgt+" +span+"WHEREb.rgt>:parentLft"; Stringhql2="update"+beanName+"bsetb.lft=b.lft+" +span+"WHEREb.lft>:parentLft"; if(!StringUtils.isBlank(tree.getTreeCondition())){ hql1+="and("+tree.getTreeCondition()+")"; hql2+="and("+tree.getTreeCondition()+")"; } super.createQuery(hql1).setInteger("parentLft",parentLft) .executeUpdate(); super.createQuery(hql2).setInteger("parentLft",parentLft) .executeUpdate(); System.out.println(hql1+"|parentLft="+parentLft); System.out.println(hql2+"|parentLft="+parentLft); //再调整自己 hql="selectb.lft,b.rgtfrom"+beanName+"bwhereb.id=:id"; position=(Object[])super.createQuery(hql).setLong("id", tree.getId()).uniqueResult(); System.out.println(hql+"|id="+tree.getId()); nodeLft=((Number)position[0]).intValue(); nodeRgt=((Number)position[1]).intValue(); intoffset=parentLft-nodeLft+1; hql="update" +beanName +"bsetb.lft=b.lft+:offset,b.rgt=b.rgt+:offsetWHEREb.lftbetween:nodeLftand:nodeRgt"; if(!StringUtils.isBlank(tree.getTreeCondition())){ hql+="and("+tree.getTreeCondition()+")"; } super.createQuery(hql).setParameter("offset",offset) .setParameter("nodeLft",nodeLft).setParameter("nodeRgt", nodeRgt).executeUpdate(); System.out.println(hql+"|offset="+offset+"|nodelft="+nodeLft+"|nodergt="+nodeRgt); //最后删除(清空位置) hql1="update"+beanName+"bsetb.rgt=b.rgt-"+span +"WHEREb.rgt>:nodeRgt"; hql2="update"+beanName+"bsetb.lft=b.lft-"+span +"WHEREb.lft>:nodeRgt"; if(tree.getTreeCondition()!=null){ hql1+="and("+tree.getTreeCondition()+")"; hql2+="and("+tree.getTreeCondition()+")"; } super.createQuery(hql1).setParameter("nodeRgt",nodeRgt) .executeUpdate(); super.createQuery(hql2).setParameter("nodeRgt",nodeRgt) .executeUpdate(); System.out.println(hql1+"|nodeRgt="+nodeRgt); System.out.println(hql2+"|nodeRgt="+nodeRgt); } } 

     3. 删除节点

     删除节点也比较简单,具体步骤为:

     A. 查找要删除节点的lft

     B. 将所有lftrgt大于删除节点lft值的都-2

     Java代码如下:

    <!--EndFragment-->

     

     

    <!--EndFragment-->Java代码
  • public void onDelete(Object entity, Serializable id, Object[] state,  
  •             String[] propertyNames, Type[] types) {  
  •         if (entity instanceof HibernateTree) {  
  •             HibernateTree tree = (HibernateTree) entity;  
  •             String beanName = tree.getClass().getName();  
  •             Session session = getSession();  
  •             FlushMode model = session.getFlushMode();  
  •             session.setFlushMode(FlushMode.MANUAL);  
  •         //查找要删除的节点的左值  
  •             String hql = "select b.lft from " + beanName + " b where b.id=:id";  
  •             Integer myPosition = (Integer) session.createQuery(hql).setLong(  
  •                     "id", tree.getId()).uniqueResult();  
  • //将所有大于删除节点左值的rgt都-2  
  •             String hql1 = "update " + beanName  
  •                     + " b set b.rgt = b.rgt - 2 WHERE b.rgt > :myPosition";  
  • //将所有大于删除节点左值的lft都-2  
  •             String hql2 = "update " + beanName  
  •                     + " b set b.lft = b.lft - 2 WHERE b.lft > :myPosition";  
  •             if (tree.getTreeCondition() != null) {  
  •                 hql1 += " and (" + tree.getTreeCondition() + ")";  
  •                 hql2 += " and (" + tree.getTreeCondition() + ")";  
  •             }  
  •             session.createQuery(hql1).setInteger("myPosition", myPosition)  
  •                     .executeUpdate();  
  •             session.createQuery(hql2).setInteger("myPosition", myPosition)  
  •                     .executeUpdate();  
  •             session.setFlushMode(model);  
  •         }  
  •     }  
  • publicvoidonDelete(Objectentity,Serializableid,Object[]state, String[]propertyNames,Type[]types){ if(entityinstanceofHibernateTree){ HibernateTreetree=(HibernateTree)entity; StringbeanName=tree.getClass().getName(); Sessionsession=getSession(); FlushModemodel=session.getFlushMode(); session.setFlushMode(FlushMode.MANUAL); //查找要删除的节点的左值 Stringhql="selectb.lftfrom"+beanName+"bwhereb.id=:id"; IntegermyPosition=(Integer)session.createQuery(hql).setLong( "id",tree.getId()).uniqueResult();//将所有大于删除节点左值的rgt都-2 Stringhql1="update"+beanName +"bsetb.rgt=b.rgt-2WHEREb.rgt>:myPosition";//将所有大于删除节点左值的lft都-2 Stringhql2="update"+beanName +"bsetb.lft=b.lft-2WHEREb.lft>:myPosition"; if(tree.getTreeCondition()!=null){ hql1+="and("+tree.getTreeCondition()+")"; hql2+="and("+tree.getTreeCondition()+")"; } session.createQuery(hql1).setInteger("myPosition",myPosition) .executeUpdate(); session.createQuery(hql2).setInteger("myPosition",myPosition) .executeUpdate(); session.setFlushMode(model); } }
     

     

     

     

    <!--EndFragment-->

     

     

    <!--EndFragment-->

     

    <!--EndFragment-->

     

    <!--EndFragment-->

     

     

    <!--EndFragment-->

     

    关于我们 | 付款方式 | 客户管理 | 网站导航 | 友情连接


    版权所有 2008 三易网络(洛阳)科技开发有限公司 京ICP备06012028号

    服务热线:0371-63653120 63658758(郑州) 0379-63921200   63265368(洛阳)

    QQ在线客服: JSP空间咨询   JSP空间咨询    Email:web@suneasy.cn

    郑州网络公司 郑州网站建设 洛阳网站建设

    总部地址:纱厂南路41号中泰新城泰福苑803室 郑州分公司地址:金水区圣菲城