Java實現,二叉樹遞歸非遞歸,前序中序後序遍歷(通俗易懂)

想要弄懂Java的一個知識點,沒有比親自手寫一遍更好的方法

仔細研究手寫一遍,一定會收穫滿滿,沒有你想想中那麼難

 小編下面以這個二叉樹爲例,測試代碼

以下是完整的四個類代碼,大家可先放在自己本地IDE上調試查看,更加清晰

我們需要先將二叉樹構建出來,然後進行遞歸非遞歸前序中序後序遍歷

//二叉樹的基本結點
public  class Node { 
		Object data;
		Node leftChild;
		Node rightChild;
		public Node(Object data, Node leftChild, Node rightChild) {
			super();
			this.data = data;
			this.leftChild = leftChild;
			this.rightChild = rightChild;
		}
	}

定義接口規範

/**
 * 定義二叉樹可以實現的基本功能
 * @author Lenovo
 *
 */
public interface Tree {
	
	public int size();
	
	public boolean isEmpty();
	
	public int getHeight();
	
	public void preTraversal();
	
	public void middleTraversal();
	
	public void postTraversal();
	
	//藉助隊列實現層次遍歷
	public void orderByQueue();
	
	//藉助棧實現非遞歸遍歷二叉樹,使用先序進行遍歷
	public void preTraByStack();
	//藉助棧實現非遞歸遍歷二叉樹,使用中序進行遍歷
	void inOrderByStack();
	//藉助棧實現非遞歸遍歷二叉樹,使用後序進行遍歷
	void postTraByStack();
}

實現代碼

/**
 * 採用雙鏈表法實現二叉樹
 * @author Lenovo
 */
public class LinkedBinaryTree  implements Tree{
	//爲此棵數,創建根節點
	private Node root;
	
	public LinkedBinaryTree(Node root) {
		super();
		this.root = root;
	}
	
	public LinkedBinaryTree() {
		super();
	}

	@Override
	public int size() {
		return this.size(root);
	}

	private int size(Node node) {
		if(node==null){
			return 0;
		}else{
			int le = size(node.leftChild);
			int ri = size(node.rightChild);
			return ri+le+1;
		}
	}

	@Override
	public boolean isEmpty() {
		return root == null;
	}

	@Override
	public int getHeight() {
		return this.getHeight(root);
	}
	
	private int getHeight(Node node) {
		if(node == null){
			return 0;
		}else{
			int ai = this.getHeight(node.leftChild);
			int bi = this.getHeight(node.rightChild);
			return ai>bi?ai+1:bi+1;
		}
	}

	@Override
	public void preTraversal() {
		//進步封裝完善方法
		//首先必須考慮代碼健壯性
		if(root !=null){
			System.out.println("先序遍歷:");
			pre(root);
			System.out.println();
		}else{
			System.out.println("此二叉樹爲空");
		}
	}
	
	private void pre(Node node) {
		if(node!=null){
		//輸出二叉樹根節點
		System.out.print(node.data+"  ");
		//繼續相應地遍歷左子樹和右子樹	
		this.pre(node.leftChild);
		this.pre(node.rightChild);
		}
	}

	@Override
	public void middleTraversal() {
		//進步封裝完善方法
		//首先必須考慮代碼健壯性
		if(root !=null){
			System.out.println("中序遍歷:");
			middle(root);
			System.out.println();
		}else{
			System.out.println("此二叉樹爲空");
		}
	}

	private void middle(Node node) {
		if(node!=null){
		//輸出二叉樹根節點
		this.middle(node.leftChild);
		System.out.print(node.data+"  ");
		this.middle(node.rightChild);
		}
	}

	@Override
	public void postTraversal() {
		//進步封裝完善方法
		//首先必須考慮代碼健壯性
		if(root !=null){
			System.out.println("中序遍歷:");
			pos(root);
			System.out.println();
		}else{
			System.out.println("此二叉樹爲空");
		}
	}

	private void pos(Node node) {
		if(node!=null){
		//輸出二叉樹根節點
		this.pos(node.leftChild);
		this.pos(node.rightChild);
		System.out.print(node.data+"  ");
		}	
	}

	@Override
	public void orderByQueue() {
		//新建一個隊列,存放二叉樹結點
		Queue qu = new LinkedList<>();
		Node node = root;
		qu.add(node);
		//遍歷輸出每一層,同時將下一層的結點加入到對列中,隊列現有的個數,就是每層節點的個數
		while(qu.size()!=0){
			for(int i = 0;i<qu.size();i++)
			{
				Node po = (Node) qu.poll();
				System.out.print(po.data+"  ");
				if(po.leftChild != null)  
					qu.add(po.leftChild);
				if(po.rightChild != null)  
					qu.add(po.rightChild);
			}
		}
		System.out.println();
	}

	@Override
	public void preTraByStack() {
		System.out.println("不採用遞歸,實現先序遍歷,藉助棧");
		//新建棧
		Deque<Node> de = new LinkedList<>();
		Node node = root;
		de.push(node);
		while(node != null&&de.size()>0){
			node = de.pop();
			System.out.print(node.data+"  ");
			if(node.leftChild!=null && node.rightChild!=null){
				de.push(node.rightChild);
				de.push(node.leftChild);
			}else if(node.leftChild!=null &&node.rightChild == null){
				de.push(node.leftChild);
			}else if(node.leftChild ==null &&node.rightChild != null){
				de.push(node.rightChild);
			}else{
				/*Node pop = de.pop();
				System.out.print(pop.data+"  ");*/
			}
		}
	}
	
	public void postTree(){
		System.out.println("藉助外部方法實現後續遍歷:");
		Deque<Node> stack =new LinkedList<Node>();
		Node node = root;
		Node proot;//標記棧頂元素前一個被訪問的元素
		int flag;//root的左孩子未被訪問;
		if(node!=null){
			do{
				while(node!=null){//將root所有左孩子全部入棧
					stack.push(root);
					node=node.leftChild;
				  }
				
				//執行到此處,棧頂元素沒有左孩子或者左子樹已經被訪問過;
				proot=null;//標記棧頂元素前一個被訪問的元素,或者此時爲最左下邊,該元素前一個被訪問的元素肯定爲空。
				flag=1;//root的左孩子已經被訪問;或者root爲null
				
				while(!stack.isEmpty() && flag==1){
					node=stack.peek();       //取到棧頂元素,但是不出棧;
					if(node.rightChild==proot){
						node=stack.pop();
						System.out.print(node.data+"  ");
						proot=node;
					}else{
						node=node.rightChild;
						flag=0;//root左邊孩子未被訪問;
					}
				}
			}while(!stack.isEmpty());
		}
	}

	@Override
	public void postTraByStack() {
		System.out.println("後序非遞歸遍歷,藉助棧");
		//新建棧,先進後出,將根結點入棧,雙端隊列
		Deque<Node> stack = new LinkedList<>();
		//新建一個list,記錄結點的狀態是否已經被訪問過
		ArrayList<Node> list = new ArrayList<>();
//		stack.push(root);
		Node proot;
		Node node = root;
		int flag;
		//首先檢查完樹的左子樹,再右子樹,最後將根節點輸出
		while(node != null  || stack.size()>0){
			//將最左子樹添加完畢
			while(node != null){
				stack.push(node);
				node = node.leftChild;
			}
			//和中序遍歷相似,爲先輸出左結點,但是做結點輸出完畢之後,不能直接將根結點彈出,而是必須先將右結點彈出,
			//最後再將根結點彈出來,就會牽扯到一個根結點的訪問狀態的問題,是否已經被遍歷過了
			//利用一個list集合記錄已將被遍歷過的根結點,防止產生死循環
			if(stack.size()> 0 ){
				Node peek = stack.peek();
				if(peek.rightChild!=null){
					boolean con = list.contains(peek);
					if(con ==true){
						Node pop = stack.pop();
						System.out.print(pop.data+"  ");
					}else{
						list.add(peek);
						node = peek.rightChild;
					}
				}else{
					Node pop = stack.pop();
					System.out.print(pop.data+"  ");
				}
			}
		}
	}
	
	@Override
	public void inOrderByStack() {
		System.out.println("中序非遞歸遍歷:");
		// 創建棧,和先序遍歷類似,直接入棧直到沒有最左左子樹可以 入棧
		Deque<Node> stack = new LinkedList<Node>();
		Node node = root;
		//添加暫時完畢,開始pop元素
		while(node!=null || stack.size()>0 ){
			
			while(node!=null){
			stack.push(node);
			node = node.leftChild;
			}
			//一邊pop並且一邊進行判斷,右結點不會null的,右子樹,繼續按照添加方法,將最左結點全部添加進去
			if(stack.size()>0){
				Node pop = stack.pop();
				System.out.print(pop.data+"  ");
				if(pop.rightChild!=null){
					node = pop.rightChild;
				}
			}
		}
		System.out.println();
	}
}

測試代碼

public class TestTree {
	
	public static void main(String[] args) {
		
		//創建一棵基本的二叉樹
		Node node7  = new Node(7, null, null);
		Node node6  = new Node(6, null, null);
		Node node3  = new Node(3, null, null);
		Node node5  = new Node(5, node6, node7);
		Node node2  = new Node(2, node3, node5);
		Node node8  = new Node(8, null, null);
		Node node11  = new Node(11, null, null);
		Node node12 = new Node(12, null, null);
		Node node10  = new Node(10, node11, node12);
		Node node9  = new Node(9, node10, null);
		Node node4  = new Node(4, node8, node9);
		
		Node root  = new Node(1, node4, node2);
//		LinkedBinaryTree link = new LinkedBinaryTree();
		LinkedBinaryTree link = new LinkedBinaryTree(root);
		
		//查看樹是否爲空
		System.out.println(link.isEmpty());
		
		//前序遞歸遍歷
		link.preTraversal();
		
		//中序遞歸遍歷
		link.middleTraversal();
		
		//後序遞歸遍歷
		link.postTraversal();
		
		//計算結點的個數
		int size = link.size();
		System.out.println("個數是:"+size);
		//得到樹的高度
		int height = link.getHeight();
		System.out.println("樹的高度是:"+height);
		//藉助隊列實現層次遍歷
		link.orderByQueue();
		
		//藉助棧實現中序遍歷,不採用遞歸
		link.inOrderByStack();
		
		//藉助棧實現先序遍歷
		link.preTraByStack();
		System.out.println();
		//藉助棧實現後續遍歷
		link.postTraByStack();
		System.out.println();
	}
}

測試結果

在實現非遞歸後序遍歷,雖然實現思路清晰,但是在性能上稍微差點,因爲每次會去list集合中,遍歷查找是否存在當前結點

小編會進一步改進

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