树状结构算法(一)

it2022-05-05  132

题目:

需求:根据传入普通实体关系转换成树形实体 要求:1.服务端函数  2.根据传入是的id和pid生成树形实体的必要字段:IsLeaf,InnerCode,OrderNoIsLeaf:叶子节点,布尔类型,如果有子节点则为false,无子节点为true InnerCode:层级码字段,字符串类型,顶级节点为00001,其子节点为0000100001,孙节点000010000100001 OrderNo:排序字段,整数类型,每个层级的先后顺序,从1递增 3.实体只有必要字段id和pid,其他字段不可写死  4.函数名称,调用方式需要自行设计  5.代码需要:整洁,易于维护 评分标准:总分10分  1.函数设计1分 2.代码整洁,.易于维护,命名规范2分 3.功能实现.6分 4.性能1分

 

示例效果:   传入实体名称org      idpidorgNameorgCode3301fa3249154ec4bd2845fa2decdcf2 同望toone6cd97261d4d74d3daaa1e2dcb6a9c6c93301fa3249154ec4bd2845fa2decdcf2银弹谷yindangu00ef7339670945679c014d9d66b8f5af6cd97261d4d74d3daaa1e2dcb6a9c6c9平台部vb80ba32eb14e4225bbf41c00e6f39e3a6cd97261d4d74d3daaa1e2dcb6a9c6c9智企部enterprise377159ffde5941b1baa30204da0fae0a3301fa3249154ec4bd2845fa2decdcf2基础设施部base63810d5f2d494efd9176361190cda373b80ba32eb14e4225bbf41c00e6f39e3a开发1组p19062f39991d24a8daa7af1004d1601f2b80ba32eb14e4225bbf41c00e6f39e3a开发2组p24b05d57ee9c941dabed8d64dede3ad50b80ba32eb14e4225bbf41c00e6f39e3a开发3组p324031eb0d275485da5330ac4b5a26a7db80ba32eb14e4225bbf41c00e6f39e3a开发4组p4e00b8cdd7e5b4df1992ac1180f4db8e200ef7339670945679c014d9d66b8f5af数据组data

 

生成实体名称org_tree            idpidorgNameorgCodeIsLeafInnerCodeOrderNo3301fa3249154ec4bd2845fa2decdcf2 同望tooneFALSE0000116cd97261d4d74d3daaa1e2dcb6a9c6c93301fa3249154ec4bd2845fa2decdcf2银弹谷yindanguFALSE0000100001100ef7339670945679c014d9d66b8f5af6cd97261d4d74d3daaa1e2dcb6a9c6c9平台部vFALSE0000100001000011b80ba32eb14e4225bbf41c00e6f39e3a6cd97261d4d74d3daaa1e2dcb6a9c6c9智企部enterpriseFALSE0000100001000022377159ffde5941b1baa30204da0fae0a3301fa3249154ec4bd2845fa2decdcf2基础设施部baseTRUE0000100002263810d5f2d494efd9176361190cda373b80ba32eb14e4225bbf41c00e6f39e3a开发1组p1TRUE0000100001000020000119062f39991d24a8daa7af1004d1601f2b80ba32eb14e4225bbf41c00e6f39e3a开发2组p2TRUE0000100001000020000224b05d57ee9c941dabed8d64dede3ad50b80ba32eb14e4225bbf41c00e6f39e3a开发3组p3TRUE00001000010000200003324031eb0d275485da5330ac4b5a26a7db80ba32eb14e4225bbf41c00e6f39e3a开发4组p4TRUE000010000100002000044e00b8cdd7e5b4df1992ac1180f4db8e200ef7339670945679c014d9d66b8f5af数据组dataTRUE000010000100001000011

 

首先我分析一下题目:

1:看题目可以看的出来,这些节点之间并不是有序的。所以第一步我们要找到根节点。

2.有两个字段关键,id,和pid。父节点的id作为子节点的pid。这样我们在循环中就要同时有一个父节点的变量,和子节点的变量。

3.当父id等于子pid时则子节点作为父节点进入下一循环查询是否有它的子节点。(递归算法)

我采用递归算法来实现,不知道有没有别的算法可以实现,如果有请在评论区留言,让我有一个进步的空间谢谢大家。

错误思维:

我认为,因为无序排列所以我认为可能在一轮循环中要找的一些节点可能会漏掉。大概是这个意思吧。其实后面想了想循环只有找到才会停止下来进入子节点,待所有子节点找寻完毕才会接着下一个查找。所以在一轮循环完毕并未有节点漏掉。

但也有例外比如我这种作法,为了优化循环我选择了删除节点元素。这样就造成我可能跳过循环。

举个例子:当我找到根节点是1的时候这时候我移除了这个元素,查找子节点开启新的循环,循环下标为0这没错就算我第二个元素变成第一个元素也没错。可如果,当我找二级元素下标为3时,当我移除这个元素集合第4个元素就变为了三。假设下标为3的元素还有子节点(被移除的元素),然后我找的这个元素的子节点为下标2。然后没找的子节点移除它。(所有元素移除前都会放入层级集合中)。这就造成我原本集合第四个元素变成了第二个元素。而这时我二级的循环应该应该进行下标为4的元素。这样的话就会造成有些元素未扫描到。

因为思维误区(所以我的代码采用while循环来演示,最佳的代码应该是for循环才对,这样代码就不会陷入无限循环中。而我接下来的代码演示中,不能存在任何除根节点以外找不到父节点的节点。否则就会陷入无限循环中。)

上代码块:

model模拟节点的一个类

package testv3; public class Model implements Cloneable { //标识1 private String id; //标识2 private String pid; //部门名称 private String orgName; //部门英文名称 private String orgCode; //是否拥有子节点 private boolean isLeaf=true; //节点坐标表示吗 private String innerCode; //层级排序 private int OrderNo; //深度 private int level; public int getLevel() { return level; } public void setLevel(int level) { this.level = level; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getPid() { return pid; } public void setPid(String pid) { this.pid = pid; } public String getOrgName() { return orgName; } public void setOrgName(String orgName) { this.orgName = orgName; } public String getOrgCode() { return orgCode; } public void setOrgCode(String orgCode) { this.orgCode = orgCode; } public boolean isLeaf() { return isLeaf; } public void setLeaf(boolean isLeaf) { this.isLeaf = isLeaf; } public String getInnerCode() { return innerCode; } public void setInnerCode(String innerCode) { this.innerCode = innerCode; } public int getOrderNo() { return OrderNo; } public void setOrderNo(int orderNo) { OrderNo = orderNo; } @Override protected Model clone() throws CloneNotSupportedException { // TODO Auto-generated method stub return (Model)super.clone(); } }

test类:

package testv3; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class test { public static void main(String[] args) { test t= new test(); t.initModel(); t.show(); } List<Model> models=new ArrayList<Model>(); private void initModel(){ Model model1=new Model(); model1.setId("3301fa3249154ec4bd2845fa2decdcf2"); model1.setPid(""); model1.setOrgName("同望"); model1.setOrgCode("toone"); models.add(model1); Model model2=new Model(); model2.setId("6cd97261d4d74d3daaa1e2dcb6a9c6c9"); model2.setPid("3301fa3249154ec4bd2845fa2decdcf2"); model2.setOrgName("银弹谷"); model2.setOrgCode("yindangu"); models.add(model2); Model model3=new Model(); model3.setId("00ef7339670945679c014d9d66b8f5af"); model3.setPid("6cd97261d4d74d3daaa1e2dcb6a9c6c9"); model3.setOrgName("平台部"); model3.setOrgCode("v"); models.add(model3); Model model4=new Model(); model4.setId("b80ba32eb14e4225bbf41c00e6f39e3a"); model4.setPid("6cd97261d4d74d3daaa1e2dcb6a9c6c9"); model4.setOrgName("智企部"); model4.setOrgCode("enterprise"); models.add(model4); Model model5=new Model(); model5.setId("377159ffde5941b1baa30204da0fae0a"); model5.setPid("3301fa3249154ec4bd2845fa2decdcf2"); model5.setOrgName("基础设施部"); model5.setOrgCode("base"); models.add(model5); Model model6=new Model(); model6.setId("63810d5f2d494efd9176361190cda373"); model6.setPid("b80ba32eb14e4225bbf41c00e6f39e3a"); model6.setOrgName("开发1组"); model6.setOrgCode("p1"); models.add(model6); Model model7=new Model(); model7.setId("9062f39991d24a8daa7af1004d1601f2"); model7.setPid("b80ba32eb14e4225bbf41c00e6f39e3a"); model7.setOrgName("开发2组"); model7.setOrgCode("p2"); models.add(model7); Model model8=new Model(); model8.setId("4b05d57ee9c941dabed8d64dede3ad50"); model8.setPid("b80ba32eb14e4225bbf41c00e6f39e3a"); model8.setOrgName("开发3组"); model8.setOrgCode("p3"); models.add(model8); Model model9=new Model(); model9.setId("24031eb0d275485da5330ac4b5a26a7d"); model9.setPid("b80ba32eb14e4225bbf41c00e6f39e3a"); model9.setOrgName("开发4组"); model9.setOrgCode("p4"); models.add(model9); Model model10=new Model(); model10.setId("e00b8cdd7e5b4df1992ac1180f4db8e2"); model10.setPid("00ef7339670945679c014d9d66b8f5af"); model10.setOrgName("数据组"); model10.setOrgCode("data"); models.add(model10); service(models); } //定义树行结构体 List<List<Model>> Nodes=new ArrayList<List<Model>>(); //记录节点中的坐标序列号 StringBuilder sb=new StringBuilder(); //逻辑方法 private void service(List<Model> models){ //定义深度 int level=0; //定义起始下标 int i=0; //记录根节点id值 String id=""; Model current1=null; while(true){ //当寻找根节点 if(models.get(i).getPid()==null||models.get(i).getPid()==""){ //当前深度1 level++; //定义当前层级节点集合 List<Model> currentModel=new ArrayList<Model>(); models.get(i).setInnerCode("00001"); models.get(i).setOrderNo(1); id=models.get(i).getId(); models.get(i).setLevel(level); //复制对象 try { current1 = models.get(i).clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } Model m=models.get(i); //移除当前节点减少循环次数 models.remove(i); //将集合中的这个节点元素赋值为空,提示垃圾回收器 m=null; //当前层级节点集合添加Model currentModel.add(current1); //所有层级节点集合放入节点集合 Nodes.add(currentModel); } //如果未获得根节点id则跳过循环 if(id==""||id==null){ //循环加1 i++; //未找的根节点跳出循环 if(i>models.size()){ break; } //跳过当前循环 continue; } //循环查找当前深度层的子节点 fathersOrder=new int[level]; fathersOrder[0]=1; foreachNode(models, level,current1,fathersOrder); fathersOrder=null; fathersOrder_Clone=null; //如果models集合没有节点则跳出循环 if(models.size()==0){ break; } } } //记录当前当前节点父节点的排序 int[] fathersOrder=null; //复制fathersOrder类似于list集合的底层复制,以一个新的数组替换旧数组 int[] fathersOrder_Clone=null; //循环加载节点 private void foreachNode(List<Model> models,int level,Model fatherNode,int[] fathersOrder){ //定义查找到子节点个数 int k=0; //定义当前节点集合 List<Model> currentModel=null; //记录循环次数 int j=0; //当节点集合中个数小于等于0时跳出循环 while(models.size()>0) { //循环结束后开启新的循环 if(j==models.size()){ j=0; } Model m=models.get(j); if(fatherNode.getId().equals(m.getPid())){ //设置有子节点false,默认为true fatherNode.setLeaf(false); //层级序列号+1 k++; //第一次进入初始化深度和该层节点集合 if(k==1){ //实例化当前节点集合 currentModel=new ArrayList<Model>(); //添加引用到所有节点集合中 Nodes.add(currentModel); //当前层级加一 level++; //System.out.println(level+"|"+fathersOrder.length); } //实例化复制节点序列数组。 fathersOrder_Clone=new int[level]; //将记录父节序列集合的数组循环复制给fathersOrder for (int i = 0; i <fathersOrder.length; i++) { fathersOrder_Clone[i]=fathersOrder[i]; } //StringBuilder清空 sb.delete(0, sb.length()); //sb中循环添加父节点序列坐标 for(int i=0;i<fathersOrder_Clone.length-1;i++){ sb.append("0000"); sb.append(fathersOrder_Clone[i]); } //添加当前序列坐标 sb.append("0000"); sb.append(k); //记录进父节点数组 fathersOrder_Clone[level-1]=k; //复制对象 Model current=null; try { current = models.get(j).clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } //设置当前层级 current.setLevel(level); //设置当前层级号 current.setInnerCode(sb.toString()); //设置当前层排序号 current.setOrderNo(k); //层节点集合添加节点 currentModel.add(current); //移除这个model,减少循环 models.remove(j); //提示垃圾回收器回收对象 m=null; //递归算是否有子节点 foreachNode(models,level,current,fathersOrder_Clone); }else{ //一轮循环结束位查找到子节点则结束当前循环 if(j==models.size()-1){ break; } //循环加1 j++; //不符合的节点跳过这次循环 continue; } } } //打印输出 private void show(){ for (List<Model> m : Nodes) { for (Model model : m) { System.out.println(model.getId()+"|"+model.getPid()+"|"+model.getOrgName()+"|"+model.getOrgCode()+"|"+model.getInnerCode()+"|"+model.isLeaf()+"|"+model.getOrderNo()); } System.out.println(); } } }

结果:

我的问题:

创建fathersOrder和fahtersOrder_Clone对象的原因是为了,在循环中因为层级不同数组的大小也不一样,并且也可以在这次循环中不需要用到fathersOrder时释放掉它。避免因为一个数组记录所有层级空间过大剩余之类的。但是最后却失败了,因为我并不知道这些数组该在什么时候初始化,并在循环快结束时将fathersOrder赋值为空,这样当内存不够时能够回收这些没用的空间。我试过将他们写在循环中和foreachNode节点中无一例外抛出异常。

所以在这里我向各位请教,该如何实现这种功能。

知识点总结:

1.实现自Cloneable接口的类,可以实现浅复制(类中引用类型引用地址没变,若要改变请将类中引用类型也实现Cloneable接口)。

2.集合与数组是引用类型的,他们的clone()方法也都是浅复制的。并且他们存放的引用类型,是存放的引用地址。

3.map中的key是可以重复的,当key相同时会比较他们打hashcode值如果这个也相同则会覆盖当前对象。另外还有一些特定的方法也可以实现,这里就不做细说了。

4.boolean类型的默认值为false;

 


最新回复(0)