二叉树有多种遍历方法,按照遍历的顺序分前序,中序,后序,层序,具体内容,不详述到处都是。按是否递归分为递归和非递归。另外还有巧妙的Morris遍历法。
今天做一实验来比较它们的效率:
构建二叉树类
public class BinTree {
public int data;
public BinTree left;
public BinTree right;
public BinTree(){
}
public BinTree(int data){
this.data=data;
}
......
}
创建二叉树方法
//根据数组创建完全二叉树
public static BinTree createTree(int[] arr){
int count=0;
int length=arr.length;
BinTree root=new BinTree(arr[count++]);
LinkedList<BinTree> queue=new LinkedList<BinTree>();
queue.add(root);
while(count<length){
BinTree lastNode=queue.poll();
if(count<length){
lastNode.left=new BinTree(arr[count++]);
queue.add(lastNode.left);
}
if(count<length){
lastNode.right=new BinTree(arr[count++]);
queue.add(lastNode.right);
}
}
return root;
}
递归前序遍历
//前序遍历,返回数列
public ArrayList<Integer> preorderTraverse(){
BinTree root=this;
ArrayList<Integer> list=new ArrayList<Integer>();
list.add(root.data);
if(root.left!=null){
list.addAll(root.left.preorderTraverse());
}
if(root.right!=null){
list.addAll(root.right.preorderTraverse());
}
return list;
}
递归中序遍历
//中序遍历,返回数列
public ArrayList<Integer> midorderTraverse(){
BinTree root=this;
ArrayList<Integer> list=new ArrayList<Integer>();
if(root.left!=null){
list.addAll(root.left.midorderTraverse());
}
list.add(root.data);
if(root.right!=null){
list.addAll(root.right.midorderTraverse());
}
return list;
}
递归后序遍历
//后序遍历返回数列
public ArrayList<Integer> afterorderTraverse(){
BinTree root=this;
ArrayList<Integer> list=new ArrayList<Integer>();
if(root.left!=null){
list.addAll(root.left.afterorderTraverse());
}
if(root.right!=null){
list.addAll(root.right.afterorderTraverse());
}
list.add(root.data);
return list;
}
非递归前序遍历
public ArrayList<Integer> preorderTraverse2(){
BinTree node=this;
ArrayList<Integer> list=new ArrayList<Integer>();
Stack<BinTree> s = new Stack<BinTree>();
while (node != null || !s.empty()) {
while (node != null) {
list.add(node.data);
s.push(node);
node = node.left;
}
if (!s.empty()) {
node = s.pop();
node = node.right;
}
}
return list;
}
非递归中序遍历
public ArrayList<Integer> midorderTraverse2(){
BinTree node =this;
ArrayList<Integer> list =new ArrayList<Integer>();
Stack<BinTree> s=new Stack<BinTree>();
while(node!=null||!s.isEmpty()){
while(node!=null){
s.push(node);
node=node.left;
}
if(!s.empty()){
node=s.pop();
list.add(node.data);
node=node.right;
}
}
return list;
}
非递归后序遍历
public ArrayList<Integer> afterorderTraverse2(){
BinTree node =this;
ArrayList<Integer> list =new ArrayList<Integer>();
Stack<BinTree> s=new Stack<BinTree>();
Stack<Integer> s2 = new Stack<Integer>();
Integer i = new Integer(1);
while (node!= null || !s.empty()) {
while (node != null) {
s.push(node);
s2.push(new Integer(0));
node = node.left;
}
while (!s.empty() && s2.peek().equals(i)) {
s2.pop();
list.add(s.pop().data);
}
if (!s.empty()) {
s2.pop();
s2.push(new Integer(1));
node = s.peek();
node = node.right;
}
}
return list;
}
非递归层序遍历
public ArrayList<Integer> levelTraverse() {
BinTree root=this;
LinkedList<BinTree> queue = new LinkedList<BinTree>();
ArrayList<Integer> list=new ArrayList<Integer>();
BinTree current = null;
queue.add(root);//将根节点入队
while(!queue.isEmpty())
{
current = queue.poll();//出队队头元素并访问
list.add(current.data);
if(current.left != null)//如果当前节点的左节点不为空入队
{
queue.add(current.left);
}
if(current.right != null)//如果当前节点的右节点不为空,把右节点入队
{
queue.add(current.right);
}
}
return list;
}
Morris中序遍历
public ArrayList<Integer> MorrisTraverse() {
ArrayList<Integer> list = new ArrayList<Integer>();
BinTree root=this;
BinTree node = root;
while(node != null) {
if(node.left == null) {
list.add(node.data);
node = node.right;
} else {
BinTree tmp = node.left;
while(tmp.right != null && tmp.right != node)
tmp = tmp.right;
if(tmp.right == null) {
tmp.right = node; //找到当前节点的前驱节点
node = node.left;
} else {
list.add(node.data);
tmp.right = null; //恢复二叉树
node = node.right;
}
}
}
return list;
}
测试
public static void main(String[] args){
//创建一个二叉树
int[] arr=new int[10000];
for(int i=0;i<arr.length;i++){
arr[i]=i;
}
BinTree tree=BinTree.createTree(arr);
//创建一个线性表存储遍历结果
ArrayList<Integer> list=new ArrayList<Integer>();
//起始时间计时
long start;
long end;
//统计每种方法的总时间
long time1,time2,time3,time4,time5,time6,time7,time8;
time1=0;
time2=0;
time3=0;
time4=0;
time5=0;
time6=0;
time7=0;
time8=0;
for(int i=0;i<100;i++){
//递归前序
start = System.nanoTime();
list=tree.preorderTraverse();
//System.out.println(list.toString());
end = System.nanoTime();
//System.out.println(end-start);
time1+=(end-start);
//递归中序遍历
start = System.nanoTime();
list=tree.midorderTraverse();
//System.out.println(list.toString());
end = System.nanoTime();
//System.out.println(end-start);
time2+=(end-start);
//递归后序
start = System.nanoTime();
list=tree.afterorderTraverse();
//System.out.println(list.toString());
end = System.nanoTime();
//System.out.println(end-start);
time3+=(end-start);
//非递归前序
start = System.nanoTime();
list=tree.preorderTraverse2();
//System.out.println(list.toString());
end = System.nanoTime();
//System.out.println(end-start);
time4+=(end-start);
//非递归中序
start = System.nanoTime();
list=tree.midorderTraverse2();
//System.out.println(list.toString());
end = System.nanoTime();
//System.out.println(end-start);
time5+=(end-start);
//非递归后序
start = System.nanoTime();
list=tree.afterorderTraverse2();
//System.out.println(list.toString());
end = System.nanoTime();
//System.out.println(end-start);
time6+=(end-start);
//非递归层序
start = System.nanoTime();
list=tree.levelTraverse();
//System.out.println(list.toString());
end = System.nanoTime();
//System.out.println(end-start);
time7+=(end-start);
//Morris中序遍历
start = System.nanoTime();
list=tree.MorrisTraverse();
//System.out.println(list.toString());
end = System.nanoTime();
//System.out.println(end-start);
time8+=(end-start);
}
System.out.println(time1);
System.out.println(time2);
System.out.println(time3);
System.out.println(time4);
System.out.println(time5);
System.out.println(time6);
System.out.println(time7);
System.out.println(time8);
}
可以通过改变数组的大小,和循环的次数来观看算法的性能是否稳定,按照10000个元素,循环100次有以下输出结果:
递归前序: 588550392
递归中序: 798815444
递归后序: 859625367
非递归前序: 177709179
非递归中序: 191600347
非递归后序: 449614968
非递归层序: 265552014
Morris: 149713899
反复试验,结果基本稳定,效率排序与二叉树元素数量有关,在各个二叉树元素数量,Morris算法都最快,其次是非递归前序和非递归中序,两者各有胜负,层序再次,递归后序遍历最慢。非递归方法普遍比递归方法快。