【Java】 二叉樹的遍歷(遞歸與循環+層序遍歷)

【Java】 大話數據結構(9) 樹(二叉樹、線索二叉樹)一文中,已經實現了採用遞歸方法的前、中、後序遍歷,本文補充了採用循環的實現方法、以及層序遍歷並進行了一個總結。html

遞歸實現

/*
 * 前序遍歷
 */
public void preOrder() {
    preOrderTraverse(root);
    System.out.println();
}  
private void preOrderTraverse(BiTNode<E> node) {
    if(node==null)
        return;
    System.out.print(node.data);
    preOrderTraverse(node.lchild);
    preOrderTraverse(node.rchild);
}
 
/*
 * 中序遍歷
 */
public void inOrder() {
    inOrderTraverse(root);
    System.out.println();
}  
private void inOrderTraverse(BiTNode<E> node) {
    if(node==null)
        return;
    inOrderTraverse(node.lchild);
    System.out.print(node.data);       
    inOrderTraverse(node.rchild);
}
 
/*
 * 後序遍歷
 */
public void postOrder() {
    postOrderTraverse(root);
    System.out.println();
}  
private void postOrderTraverse(BiTNode<E> node) {
    if(node==null)
        return;
    postOrderTraverse(node.lchild);            
    postOrderTraverse(node.rchild);
    System.out.print(node.data);
}

非遞歸實現(迭代)

   非遞歸實現,須要先建立一個棧,利用其特性來進行儲存和輸出。java

  • 前序遍歷,先輸出當前點的值,一直沿着左子樹進行讀取,沒左子樹就在右子樹重複上述過程。
  • 中序遍歷與前序遍歷基本一致,只是輸出值的代碼位置不一樣。
  • 後序遍歷因爲要左右子樹輸出完後才能輸出根結點,因此增長一個棧進行標記是否完成左右子樹的輸出,其他思想基本相似。

  下面代碼中,要注意node的結點位置和stack.peek()的位置關係。node

  此外,在後序非遞歸遍歷的過程當中,棧中保留的是當前結點的全部祖先。這是和先序及中序遍歷不一樣的。在某些和祖先有關的算法中,此算法頗有價值。算法

	/**
	 * 前序遍歷(非遞歸)
	 */
	public void preOrder2() {
		preOrder2(root);
		System.out.println();
	}
	private void preOrder2(BiTNode node) {
		Stack<BiTNode> stack = new Stack<BiTNode>();
		while(node!=null||!stack.isEmpty()) {
			while(node!=null) {
				System.out.print(node.data);
				stack.push(node);
				node=node.lchild;
			}
			node=stack.pop().rchild;		
		}
	}
	
	
	/**
	 * 中序遍歷
	 */	
	public void inOrder2() {
		inOrder2(root);
		System.out.println();
	}
	private void inOrder2(BiTNode node) {
		Stack<BiTNode> stack = new Stack<BiTNode>();
		while(node!=null||!stack.isEmpty()) {
			while(node!=null) {
				stack.push(node);
				node=node.lchild;
			}					
			node=stack.pop();
			System.out.print(node.data);
			node=node.rchild;
		}		
	}
	
	
	/**
	 * 後序遍歷
	 */	
	public void postOrder2() {
		postOrder2(root);
		System.out.println();
	}
	private void postOrder2(BiTNode node) {
		Stack<BiTNode> stack = new Stack<BiTNode>();
		Stack<Integer> tag = new Stack<Integer>(); 
//下面這段註釋也能實現,與後面未註釋部分基本一致。表明了本身的思考過程,就不刪除了 // while(node!=null||!stack.isEmpty()) { // while(node!=null){ // stack.push(node); // tag.push(0); // node=node.lchild; // } //註釋中的tag用於標記當前結點是否完成左右子結點遍歷(因此用0,1表示) // while(!tag.isEmpty()&&tag.peek()==1) { //棧頂節點的左右子結點已完成遍歷 // System.out.print(stack.pop().data); // tag.pop(); // } // if(!tag.isEmpty()) { //上面和這裏的 !flag.isEmpty() 不可省略,否則會出錯。 // tag.pop(); // tag.push(1); // node=stack.peek().rchild; // } // } /*後序遍歷時,分別從左子樹和右子樹共兩次返回根結點(用tag表示次數), * 只有從右子樹返回時才訪問根結點,因此增長一個棧標記到達結點的次序。 */ while(node!=null||!stack.isEmpty()) { if(node!=null){ stack.push(node); tag.push(1); //第一次訪問 node=node.lchild; }else { if(tag.peek()==2) { System.out.print(stack.pop().data); tag.pop(); }else { tag.pop(); tag.push(2); //第二次訪問 node=stack.peek().rchild; } } } }

  

20191104:前序和後序的非遞歸遍歷還能夠合理利用棧的性質來實現,與上面的稍有不一樣。數據結構

前序:ide

    public List<Integer> preorderTraversal(TreeNode root) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        Stack<TreeNode> stk = new Stack<>();
        stk.push(root);
        while(!stk.isEmpty()){
            TreeNode node = stk.pop();
            if(node==null)
                continue;
            list.add(node.val);
            stk.push(node.right);
            stk.push(node.left);
        }
        return list;
    }

 

後序:post

    public List<Integer> postorderTraversal(TreeNode root) {
        LinkedList<Integer> list = new LinkedList<Integer>();
        Stack<TreeNode> stk = new Stack<>();
        stk.push(root);
        while(!stk.isEmpty()){
            TreeNode node = stk.pop();
            if(node==null)
                continue;
            list.addFirst(node.val);  //LinkedList's method. If using ArrayList here,then using 'Collections.reverse(list)'' in the end;
            stk.push(node.left);
            stk.push(node.right);
        }
        return list;
    }

  

層序遍歷

  合理利用隊列的性質便可。測試

	public void levelOrder() {
		BiTNode<E> node =root;
		LinkedList<BiTNode<E>> list = new LinkedList<>();
		list.add(node);
		while(!list.isEmpty()) {
			node=list.poll();
			System.out.print(node.data);
			if(node.lchild!=null)
				list.offer(node.lchild);
			if(node.rchild!=null)
				list.offer(node.rchild);
		}
	}

  

完整代碼(含測試代碼)

package BiTree;

import java.util.LinkedList;
import java.util.Stack;

class BiTNode<E>{
	E data;
	BiTNode<E> lchild,rchild;		
	public BiTNode(E data) {
		this.data=data;
		this.lchild=null;
		this.rchild=null;
	}
}

public class BiTree<E> {
	
	private BiTNode<E> root;

	public BiTree() {
		//root=new BiTNode(null, null, null);
		root=null;
	}
	
	/*
	 * 前序遍歷
	 */
	public void preOrder() {
		preOrderTraverse(root);
		System.out.println();
	}	
	private void preOrderTraverse(BiTNode<E> node) {
		if(node==null)
			return;
		System.out.print(node.data);
		preOrderTraverse(node.lchild);
		preOrderTraverse(node.rchild);
	}
	
	/*
	 * 中序遍歷
	 */
	public void inOrder() {
		inOrderTraverse(root);
		System.out.println();
	}	
	private void inOrderTraverse(BiTNode<E> node) {
		if(node==null)
			return;
		inOrderTraverse(node.lchild);
		System.out.print(node.data);		
		inOrderTraverse(node.rchild);
	}
	
	/*
	 * 後序遍歷
	 */
	public void postOrder() {
		postOrderTraverse(root);
		System.out.println();
	}	
	private void postOrderTraverse(BiTNode<E> node) {
		if(node==null)
			return;
		postOrderTraverse(node.lchild);				
		postOrderTraverse(node.rchild);
		System.out.print(node.data);
	}
	
	
	//===============循環遍歷===============
	/**
	 * 前序遍歷(非遞歸)
	 */
	public void preOrder2() {
		preOrder2(root);
		System.out.println();
	}
	private void preOrder2(BiTNode node) {
		Stack<BiTNode> stack = new Stack<BiTNode>();
		while(node!=null||!stack.isEmpty()) {
			while(node!=null) {
				System.out.print(node.data);
				stack.push(node);
				node=node.lchild;
			}
			node=stack.pop().rchild;		
		}
	}
	
	
	/**
	 * 中序遍歷
	 */	
	public void inOrder2() {
		inOrder2(root);
		System.out.println();
	}
	private void inOrder2(BiTNode node) {
		Stack<BiTNode> stack = new Stack<BiTNode>();
		while(node!=null||!stack.isEmpty()) {
			while(node!=null) {
				stack.push(node);
				node=node.lchild;
			}					
			node=stack.pop();
			System.out.print(node.data);
			node=node.rchild;
		}		
	}
	
	
	/**
	 * 後序遍歷
	 */	
	public void postOrder2() {
		postOrder2(root);
		System.out.println();
	}
	private void postOrder2(BiTNode node) {
		Stack<BiTNode> stack = new Stack<BiTNode>();
		Stack<Integer> tag = new Stack<Integer>(); 	
//		while(node!=null||!stack.isEmpty()) {
//			while(node!=null){
//				stack.push(node);
//				tag.push(0);
//				node=node.lchild;
//			}
			//這裏的tag用於標記當前結點是否完成左右子結點遍歷(因此用0,1表示)
//			while(!tag.isEmpty()&&tag.peek()==1) {  //棧頂節點的左右子結點已完成遍歷
//				System.out.print(stack.pop().data);
//				tag.pop();
//			}
//			if(!tag.isEmpty()) {   //上面和這裏的 !flag.isEmpty() 不可省略,否則會出錯。
//				tag.pop();
//				tag.push(1);
//				node=stack.peek().rchild;
//			}			
//		}
		/*後序遍歷時,分別從左子樹和右子樹共兩次返回根結點(用tag表示次數),
		 * 只有從右子樹返回時才訪問根結點,因此增長一個棧標記到達結點的次序。
		 */
		while(node!=null||!stack.isEmpty()) {
			if(node!=null){
				stack.push(node);
				tag.push(1);  //第一次訪問
				node=node.lchild;
			}else {
				if(tag.peek()==2) {
					System.out.print(stack.pop().data);
					
					tag.pop();
				}else {
					tag.pop();
					tag.push(2); //第二次訪問
					node=stack.peek().rchild;
				}			
			}		
		}
	}
	
	
	//=========層序遍歷============
	public void levelOrder() {
		BiTNode<E> node =root;
		LinkedList<BiTNode<E>> list = new LinkedList<>();
		list.add(node);
		while(!list.isEmpty()) {
			node=list.poll();
			System.out.print(node.data);
			if(node.lchild!=null)
				list.offer(node.lchild);
			if(node.rchild!=null)
				list.offer(node.rchild);
		}
	}
		
	
	public static void main(String[] args) {
		BiTree<String> aBiTree = new BiTree<String>();
		aBiTree.root=new BiTNode<String>("A");
		aBiTree.root.lchild=new BiTNode<String>("B");
		aBiTree.root.rchild=new BiTNode<String>("C");
		aBiTree.root.lchild.rchild=new BiTNode<String>("D");

//		BiTree<String> aBiTree = new BiTree<String>();
//		aBiTree.root=new BiTNode("A");
//		aBiTree.root.lchild=new BiTNode("B");
//		aBiTree.root.lchild.lchild=new BiTNode("C");
//		aBiTree.root.lchild.lchild.lchild=new BiTNode("D");
//		aBiTree.root.lchild.rchild=new BiTNode("E");
//		aBiTree.root.lchild.rchild.lchild=new BiTNode("F");
//		aBiTree.root.lchild.rchild.lchild.rchild=new BiTNode("G");
//		aBiTree.root.lchild.rchild.lchild.rchild.rchild=new BiTNode("H");
		
		System.out.println("————前序————");
		aBiTree.preOrder();
		aBiTree.preOrder2();
		System.out.println("————中序————");
		aBiTree.inOrder();
		aBiTree.inOrder2();
		System.out.println("————後序————");
		aBiTree.postOrder();
		aBiTree.postOrder2();
		System.out.println("————層序遍歷————");
		aBiTree.levelOrder();
	}		
}

 

————前序————
ABDC
ABDC
————中序————
BDAC
BDAC
————後序————
DBCA
DBCA
————層序遍歷————
ABCD
遍歷結果

 

 

參考:經常使用數據結構算法:二叉樹的遍歷(遞歸和非遞歸)this

相關文章
相關標籤/搜索