实战算法——多叉树全路径遍历 (2)

在这个树中,每个节点都有childs,如果是叶子节点,则childs中的size为0,这是下面判断一个节点是否为叶子节点的重要依据接下来我们会对核心算法代码进行实现。

Main类

public class Main { public static void main(String[] args) { MultiTree tree = new MultiTree(); List<String> path1 = tree.listAllPathByRecursion(); System.out.println(path1); List<String> path2 = tree.listAllPathByNotRecursion(); System.out.println(path2); } } 递归方法

需要完善MultiTree类中的listAllPathByRecursion方法和listPath方法

递归过程方法:listAllPathByRecursion

算法流程图如下:

1559034803163

代码实现如下:

public void listPath(TreeNode root,String path){ if(root.getChilds().isEmpty()){//叶子节点 path = path + root.getContent(); pathList.add(path); //将结果保存在list中 return; }else{ //非叶子节点 path = path + root.getContent() + "->"; //进行子节点的递归 HashMap<String, TreeNode> childs = root.getChilds(); Iterator iterator = childs.entrySet().iterator(); while(iterator.hasNext()){ Map.Entry entry = (Map.Entry)iterator.next(); TreeNode childNode = (TreeNode) entry.getValue(); listPath(childNode,path); } } }

递归调用方法:listAllPathByRecursion

public List<String> listAllPathByRecursion(){ //清空路径容器 this.pathList.clear(); listPath(this.root,""); return this.pathList; } 非递归方法

非递归方法的代码量和递归方法一比,简直是太多了,而且内容不好理解,不知道大家能不能看懂我写的代码,我已经尽力写上相关注释了。

首先建立了两个栈,示意图如下,栈的实现使用Deque,需要注意的是代码中的空指针情况。

主栈:用于处理节点和临时路径的存储,主栈为空时说明,节点处理完毕

副栈:用于存放待处理节点,副栈为空时说明,节点遍历完毕

1559035722019

其他相关变量介绍:

popCount :用于存储一个节点的子节点的弹出个数。例如r有3个子节点,如果r对应的弹出个数为3,说明r的叶子节点处理完毕,可以弹出r。因为r弹出后,主栈没有元素,故处理完毕。

curString:用于存储临时路径,当主栈元素变化时,curString也会进行变化,例如上图curString为“r->g->”,当栈顶元素弹出时,需要减去"g->"。如果栈顶元素是叶子节点说明该条路径已经遍历完成,需要添加到path路径容器中。

程序流程图:

非递归流程图

具体实现代码如下:

/** * 非递归方法输出所有路径 */ public List<String> listAllPathByNotRecursion(){ //清空路径容器 this.pathList.clear(); //主栈,用于计算处理路径 Deque<TreeNode> majorStack = new ArrayDeque(); //副栈,用于存储待处理节点 Deque<TreeNode> minorStack = new ArrayDeque(); minorStack.addLast(this.root); HashMap<String,Integer> popCount = new HashMap<>(); String curString = ""; while(!minorStack.isEmpty()){ //出副栈,入主栈 TreeNode minLast = minorStack.pollLast(); majorStack.addLast(minLast); curString+=minLast.getContent()+"->"; //将该节点的子节点入副栈 if(!minLast.getChilds().isEmpty()){ HashMap<String, TreeNode> childs = minLast.getChilds(); Iterator iterator = childs.entrySet().iterator(); while(iterator.hasNext()){ Map.Entry entry = (Map.Entry)iterator.next(); TreeNode childNode = (TreeNode) entry.getValue(); minorStack.addLast(childNode); } } //出主栈 TreeNode majLast = majorStack.peekLast(); //循环条件:栈顶为叶子节点 或 栈顶节点孩子节点遍历完了(需要注意空指针问题) while(majLast.getChilds().size() ==0 || (popCount.get(majLast.getContent())!=null && popCount.get(majLast.getContent()).equals(majLast.getChilds().size()))){ TreeNode last = majorStack.pollLast(); majLast = majorStack.peekLast(); if(majLast == null){ //此时主栈为空,运算完毕 return this.pathList; } if(popCount.get(majLast.getContent())==null){//第一次弹出孩子节点,弹出次数设为1 popCount.put(majLast.getContent(),1); }else{ //非第一次弹出孩子节点,在原有基础上加1 popCount.put(majLast.getContent(),popCount.get(majLast.getContent())+1); } String lastContent = last.getContent(); if(last.getChilds().isEmpty()){//如果是叶子节点才将结果加入路径集中 this.pathList.add(curString.substring(0,curString.length()-2)); } //调整当前curString,减去2是减的“->”这个符号 curString = curString.substring(0,curString.length()-lastContent.length()-2); } } return this.pathList; } 测试

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyyxwx.html