【数据结构】树的先序遍历、后序遍历以及广度优先遍历
树的遍历分为深度优先遍历和广度优先遍历。在二叉树中,深度优先遍历分为先序遍历、中序遍历和后续遍历;而在树中,因为子节点右多个,因此中序 遍历没有参考性,所以只有先序遍历和后序遍历。
在这里,主要贴一下树的先序遍历、后续遍历以及广度优先遍历。
TreeNode<T>是定义树的节点的接口,包含以下行为:
1、获取子节点列表
2、获取真正的类型
其中T为泛型,传入的应该是节点的真正的实现类,为了通过getValue后返回实现类方便后续调用实现类的私有实现;
package com.keronimbus.generic; import java.io.Serializable; import java.util.List; public interface TreeNode<T> extends Serializable { List<TreeNode<T>> getChildren(); T getValue(); }
TreeVisitor<T>是三种遍历的实现,返回的结果是包含访问顺序的List
package com.keronimbus.generic; import java.util.*; public class TreeVisitor<T> { /** * 先序遍历 * @param root 树的根节点 * @return */ public List<T> preOrderTraversal(TreeNode<T> root){ Stack<TreeNode<T>> stack = new Stack<>(); if(root == null){ return null; } Map<TreeNode<T>, Iterator<TreeNode<T>>> iteratorMap = new HashMap<>(); Map<TreeNode<T>, Boolean> visitedFlag = new HashMap<>(); stack.push(root); TreeNode<T> cursor = null; List<T> traversalOrder = new ArrayList<>(); while(!stack.empty()){ cursor = stack.pop(); if (visitedFlag.get(cursor) == null) { visitedFlag.put(cursor,Boolean.TRUE); traversalOrder.add(cursor.getValue()); } List<TreeNode<T>> childrenList = cursor.getChildren(); if(childrenList== null || childrenList.size()<=0){ continue; } Iterator<TreeNode<T>> childIterator = iteratorMap.get(cursor); if(childIterator == null){ childIterator = childrenList.iterator(); iteratorMap.put(cursor, childIterator); } if(childIterator.hasNext()){ TreeNode<T> child = childIterator.next(); stack.push(cursor); stack.push(child); } } return traversalOrder; } /** * 后序遍历 * @param root 树的根节点 * @return */ public List<T> postOrderTraversal(TreeNode<T> root){ Stack<TreeNode<T>> stack = new Stack<>(); if(root == null){ return null; } Map<TreeNode<T>, Iterator<TreeNode<T>>> iteratorMap = new HashMap<>(); Map<TreeNode<T>, Boolean> visitedFlag = new HashMap<>(); stack.push(root); TreeNode<T> cursor = null; List<T> traversalOrder = new ArrayList<>(); while(!stack.empty()){ cursor = stack.pop(); List<TreeNode<T>> childrenList = cursor.getChildren(); if(childrenList== null || childrenList.size()<=0){ traversalOrder.add(cursor.getValue()); continue; } Iterator<TreeNode<T>> childIterator = iteratorMap.get(cursor); if(childIterator == null){ childIterator = childrenList.iterator(); iteratorMap.put(cursor, childIterator); } if(childIterator.hasNext()){ TreeNode<T> child = childIterator.next(); stack.push(cursor); stack.push(child); }else{ traversalOrder.add(cursor.getValue()); } } return traversalOrder; } /** * 广度优先遍历 * @param root 树的根节点 * @return */ public List<T> breadFirstTraversal(TreeNode<T> root){ if(root == null){ return null; } Queue<TreeNode<T>> queue = new ArrayDeque<>(); queue.add(root); List<T> traversalOrder = new ArrayList<>(); while(queue.size()>0){ TreeNode<T> cursor = queue.poll(); traversalOrder.add(cursor.getValue()); List<TreeNode<T>> children = cursor.getChildren(); if(children!= null && children.size()>0){ queue.addAll(children); } } return traversalOrder; } }
在使用的时候,节点的需要实现TreeNode<T>接口,并且指定实现类的类型
public static void main(String[] args){ Department department1 = new Department(1,"1-1",1); Department department2 = new Department(2,"2-1",2); Department department3 = new Department(3,"3-1",3); Department department4 = new Department(4,"2-2",2); List<TreeNode<Department>> childrenList1= new ArrayList<>(); childrenList1.add(department2); childrenList1.add(department4); List<TreeNode<Department>> childrenList2 = new ArrayList<>(); childrenList2.add(department3); department1.setChildren(childrenList1); department2.setChildren(childrenList2); TreeVisitor<Department> visitor = new TreeVisitor<>(); List<Department> preOrder = visitor.preOrderTraversal(department1); preOrder.forEach(item->System.out.println(item)); System.out.println("--------------------"); List<Department> postOrder = visitor.postOrderTraversal(department1); postOrder.forEach(item->System.out.println(item)); System.out.println("--------------------"); List<Department> breadFirstOrder = visitor.breadFirstTraversal(department1); breadFirstOrder.forEach(item->System.out.println(item)); }
结果:
树的真实结构:
1-1
2-1 2-2
3-1
这种实现本身是为了不希望调用方在使用的时候用对象本身重新包装一个TreeNode,但是这种实现看来并没有很优雅,目前没有想到更好的实现办法。
顺便一说,不知道Java中为什么没有树结构。