1. 程式人生 > >二叉樹的非遞迴先序,中序,後序遍歷

二叉樹的非遞迴先序,中序,後序遍歷

前幾天面試美團的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());
	}
}

覺得比較實在的點個關注,一起加油努力學習