二叉树的非递归先序,中序,后序遍历

前几天面试美团的java后台岗位,第一题就是手写二叉树非递归先序遍历,当时我就不乐意了。然后其实能想出来的,但是没私底下实现过,还真没把握给面试官,最后挂了。所以痛定思痛,我们来手写一下二叉树的非递归先序,中序和后序遍历,并对其中的逻辑部分进行步骤的讲解,我觉得一切的东西你只要给他赋予一定的规则,那么它就没那么难了(后面的部分不适合对数据结构不熟悉的同学)

    	/**
    	 * 非递归先序遍历
    	 * @param root
    	 */
        public void depthOrderTraversal(BinTree root){
        	if(root==null){
        		System.out.println("null");
        		return;
        	}
        	ArrayDeque<BinTree> stack=new ArrayDeque<BinTree>();
        	stack.push(root);
        	while(stack.isEmpty()==false){
        		BinTree node = stack.pop();
        		System.out.print(node.getData()+" ");
        		if(node.rChild!=null)
        			stack.push(node.rChild);
        		if(node.lChild!=null)
        			stack.push(node.lChild);
        	}
        }

非递归先序遍历需要借用栈(后进先出)这种数据结构,下面我梳理一下这个过程,分步骤ABC

A:将根节点传入进方法中,也就是当作参数传递过来,你可以在节点类中写一个getRoot()方法,返回他的节点对象

B:判断节点是否为空,为空就返回return;(什么都不用返回)。因为return;这样这个方法就结束了;((●'◡'●))

接下来的C-D我们讲解一下从栈到实现先序遍历的整个过程的逻辑梳理

C:创建一个栈,并将根节点率先加入到栈中(这里为什么用ArrayDeque来创建栈呢?因为网上说用做栈的时候性能比Stack好)

D:这里我们思考一个问题,到底用什么来做循环的条件,首先我们现在有两个东西在手里,一个栈,一个根节点数据,能改变的貌似也只有栈的size()了,所以我们通过判断栈是否为空来循环我们的先序遍历的逻辑部分

E-G我们就用三句话来简短的叙述一下过程

E:栈顶出栈并输出

F:“出栈”右孩子入栈(E中出栈的那个元素称为出栈)

G:”出栈“左孩子入栈

然后E-F整个过程包括在while循环当中,当栈中元素全部出栈的时候循环结束。

 

接下来我们讲一下二叉树非递归中序遍历

        /**
    	 * 非递归中序遍历
    	 * @param root
    	 */
        public void depthMidTraversal(BinTree root) {
            Stack<BinTree> s = new Stack<BinTree>();
            BinTree p = root;
            while(p != null || !s.empty()) {
                while (p != null) {
                   s.push(p);
                   p = p.lChild;
                }
                p = s.pop();
                System.out.print(p.getData()+" ");
                if (p.rChild != null) {
                    p = p.rChild;
                }
                else p = null;
           } 
        }

下面我还是通过A-F的步骤讲解一下非递归中序遍历的过程

A:首先创建一个栈s (BinTree p = root 方便后序编写,我没试过直接用root来写行不行,我感觉应该按道理,逻辑来说应该不行)

B:确定循环条件,p节点不为空或者栈不为空

C-F:中间这个步骤我统一说一下,其实我支持大家通过Dubug来看一下这个过程,我想了想要用语言阐述太为难我这个理科生了,中间逻辑大家看代码吧。

 

非递归后序遍历

/**
    	 * 非递归后序遍历
    	 * @param root
    	 */
        public void depthAfterTraversal(BinTree root){
    		Stack<BinTree> s = new Stack<BinTree>();
    		BinTree p = root;
    		while (p != null || !s.empty()) {
    			while(p != null) {
    				s.push(p);
    				p = p.lChild;
    			}
    			p = s.pop();
    			System.out.print(p.getData()+" ");
    			//这里需要判断一下,当前p是否为栈顶的左子树,如果是的话那么还需要先访问右子树才能访问根节点
    			//如果已经是不是左子树的话,那么说明左右子书都已经访问完毕,可以访问根节点了,所以讲p复制为NULL
    			//取根节点
    			if (!s.empty() && p == s.peek().lChild) {
    				p = s.peek().rChild;
    			}
    			else p = null;
    		}
        }

 

 

我把全部代码粘贴供大家参考,其中也有递归实现先序,中序,后序的方法,大家加油

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.Vector;

public class BinTree {
    	private BinTree lChild;//左孩子
    	private BinTree rChild;//右孩子
    	private BinTree root;//根节点
    	private Object data; //数据域
    	private List<BinTree> datas;//存储所有的节点
    	private Vector<Vector<Integer>> vec; 
    	private Vector<Integer> son;
    	public BinTree(BinTree lChild, BinTree rChild, Object data) {
    		super();
    		this.lChild = lChild;
    		this.rChild = rChild;
    		this.data = data;
    	}
    	public BinTree(Object data) {
    		this(null, null, data);
    	}
    	public BinTree() {
    		super();
    	}
    	
    	public void createTree(Object[] objs){
    		datas=new ArrayList<BinTree>();
    		for (Object object : objs) {
    			datas.add(new BinTree(object));
    		}
    		root=datas.get(0);//将第一个作为根节点
    		for (int i = 0; i < objs.length/2; i++) {
    			datas.get(i).lChild=datas.get(i*2+1);
    			if(i*2+2<datas.size()){//避免偶数的时候 下标越界
    				datas.get(i).rChild=datas.get(i*2+2);
    			}
    		}
    	}
    	//先序遍历
    	public void preorder(BinTree root){
    		if(root!=null){
    			visit(root.getData());
    			preorder(root.lChild);
    			preorder(root.rChild);
    		}
    		
    	}
    	//中序遍历
    	public void inorder(BinTree root){
    		if(root!=null){
    			inorder(root.lChild);
    			visit(root.getData());
    			inorder(root.rChild);
    		}
    		
    	}
    	//后序遍历
    	public void afterorder(BinTree root){
    		if(root!=null){
    			afterorder(root.lChild);
    			afterorder(root.rChild);
    			visit(root.getData());
    		}
    		
    	}
    	
    	/**
    	 * 非递归先序遍历
    	 * @param root
    	 */
        public void depthOrderTraversal(BinTree root){
        	if(root==null){
        		System.out.println("null");
        		return;
        	}
        	ArrayDeque<BinTree> stack=new ArrayDeque<BinTree>();
        	stack.push(root);
        	while(stack.isEmpty()==false){
        		BinTree node = stack.pop();
        		System.out.print(node.getData()+" ");
        		if(node.rChild!=null)
        			stack.push(node.rChild);
        		if(node.lChild!=null)
        			stack.push(node.lChild);
        	}
        }
        	
        	
        /**
    	 * 非递归中序遍历
    	 * @param root
    	 */
        public void depthMidTraversal(BinTree root) {
            Stack<BinTree> s = new Stack<BinTree>();
            BinTree p = root;
            while(p != null || !s.empty()) {
                while (p != null) {
                   s.push(p);
                   p = p.lChild;
                }
                p = s.pop();
                System.out.print(p.getData()+" ");
                if (p.rChild != null) {
                    p = p.rChild;
                }
                else p = null;
           } 
        }
        
    	/**
    	 * 非递归后序遍历
    	 * @param root
    	 */
        public void depthAfterTraversal(BinTree root){
    		Stack<BinTree> s = new Stack<BinTree>();
    		BinTree p = root;
    		while (p != null || !s.empty()) {
    			while(p != null) {
    				s.push(p);
    				p = p.lChild;
    			}
    			p = s.pop();
    			System.out.print(p.getData()+" ");
    			//这里需要判断一下,当前p是否为栈顶的左子树,如果是的话那么还需要先访问右子树才能访问根节点
    			//如果已经是不是左子树的话,那么说明左右子书都已经访问完毕,可以访问根节点了,所以讲p复制为NULL
    			//取根节点
    			if (!s.empty() && p == s.peek().lChild) {
    				p = s.peek().rChild;
    			}
    			else p = null;
    		}
        }
        
        
    	
    	//层序遍历
//    	public void levelOrder(BinTree root) {
//    		if(root!=null){
//    			
//    			Queue<Pair<BinTree,Integer>> q = new LinkedList<>();
//    			Map<BinTree,Integer> record = new HashMap<>();
//    			record.put(root, 0);
//    			q.offer(record);
//    			while(!q.isEmpty()){
//    				 int level = q.peek().values();
//    				
//    			}
//    		}
//    		
//    		
//    	}
    	private void visit(Object obj) {
    		System.out.print(obj+" ");
    	}
    	public Object getData() {
    		return data;
    	}
    	public BinTree getRoot() {
    		return root;
    	}
    	
    }

下面是调用这个类的main方法,大家加油,用Dubug看一下这个运行过程,比我用语言阐述要强的多


public class TestTree {
	public static void main(String[] args) {
		BinTree binTree=new BinTree();
		Object[] objs={0,1,2,3,4,5,6,7};
		binTree.createTree(objs);
//		binTree.preorder(binTree.getRoot()); 先序遍历
//		binTree.inorder(binTree.getRoot()); 中序遍历
//		binTree.afterorder(binTree.getRoot()); //后序遍历
//		binTree.depthOrderTraversal(binTree.getRoot()); //非递归先序遍历
//		binTree.depthMidTraversal(binTree.getRoot());//非递归中序遍历
		binTree.depthAfterTraversal(binTree.getRoot());
	}
}

觉得比较实在的点个关注,一起加油努力学习

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章