1. 程式人生 > >二叉樹的前序遍歷,中序遍歷,後序遍歷(Java實現)

二叉樹的前序遍歷,中序遍歷,後序遍歷(Java實現)

1.前序遍歷

    前序遍歷(DLR,lchild,data,rchild),是二叉樹遍歷的一種,也叫做先根遍歷、先序遍歷、前序周遊,可記做根左右。前序遍歷首先訪問根結點然後遍歷左子樹,最後遍歷右子樹。

前序遍歷首先訪問根結點然後遍歷左子樹,最後遍歷右子樹。在遍歷左、右子樹時,仍然先訪問根結點,然後遍歷左子樹,最後遍歷右子樹。若二叉樹為空則結束返回,否則:(1)訪問根結點。(2)前序遍歷左子樹(3)前序遍歷右子樹 。前序遍歷前序遍歷需要注意的是:遍歷左右子樹時仍然採用前序遍歷方法。如右圖所示二叉樹前序遍歷結果:ABDECF已知後序遍歷和中序遍歷,就能確定前序遍歷。

    其實在遍歷二叉樹的時候有三次遍歷, 比如前序遍歷:A->B->D->D(D左子節點並返回到D)->D(D右子節點並返回到D)->B->E->E(左)->E(右)->->B->A->C->F->F(左)->F(右)->C->C(右),所以可以用棧結構,把遍歷到的節點壓進棧,沒子節點時再出棧。也可以用遞迴的方式,遞迴的輸出當前節點,然後遞迴的輸出左子節點,最後遞迴的輸出右子節點。直接看程式碼更能理解:

package test;
//前序遍歷的遞迴實現與非遞迴實現
import java.util.Stack;
public class Test 
{
	public static void main(String[] args)
	{
		TreeNode[] node = new TreeNode[10];//以陣列形式生成一棵完全二叉樹
		for(int i = 0; i < 10; i++)
		{
			node[i] = new TreeNode(i);
		}
		for(int i = 0; i < 10; i++)
		{
			if(i*2+1 < 10)
				node[i].left = node[i*2+1];
			if(i*2+2 < 10)
				node[i].right = node[i*2+2];
		}
		
		preOrderRe(node[0]);
	}
	
	public static void preOrderRe(TreeNode biTree)
	{//遞迴實現
		System.out.println(biTree.value);
		TreeNode leftTree = biTree.left;
		if(leftTree != null)
		{
			preOrderRe(leftTree);
		}
		TreeNode rightTree = biTree.right;
		if(rightTree != null)
		{
			preOrderRe(rightTree);
		}
	}
	
	public static void preOrder(TreeNode biTree)
	{//非遞迴實現
		Stack<TreeNode> stack = new Stack<TreeNode>();
		while(biTree != null || !stack.isEmpty())
		{
			while(biTree != null)
			{
				System.out.println(biTree.value);
				stack.push(biTree);
				biTree = biTree.left;
			}
			if(!stack.isEmpty())
			{
				biTree = stack.pop();
				biTree = biTree.right;
			}
		}
	}
}

class TreeNode//節點結構
{
	int value;
	TreeNode left;
	TreeNode right;
	
	TreeNode(int value)
	{
		this.value = value;
	}
}



2.中序遍歷

中序遍歷(LDR)是二叉樹遍歷的一種,也叫做中根遍歷、中序周遊。在二叉樹中,先左後根再右。巧記:左根右。中序遍歷首先遍歷左子樹,然後訪問根結點,最後遍歷右子樹若二叉樹為空則結束返回,否則:
  (1)中序遍歷左子樹(2)訪問根結點(3)中序遍歷右子樹如右圖所示二叉樹中序遍歷結果:DBEAFC
import java.util.Stack;
public class Test 
{
	public static void main(String[] args)
	{
		TreeNode[] node = new TreeNode[10];//以陣列形式生成一棵完全二叉樹
		for(int i = 0; i < 10; i++)
		{
			node[i] = new TreeNode(i);
		}
		for(int i = 0; i < 10; i++)
		{
			if(i*2+1 < 10)
				node[i].left = node[i*2+1];
			if(i*2+2 < 10)
				node[i].right = node[i*2+2];
		}
		
		midOrderRe(node[0]);
		System.out.println();
		midOrder(node[0]);
	}
	
	public static void midOrderRe(TreeNode biTree)
	{//中序遍歷遞迴實現
		if(biTree == null)
			return;
		else
		{
			midOrderRe(biTree.left);
			System.out.println(biTree.value);
			midOrderRe(biTree.right);
		}
	}
	
	
	public static void midOrder(TreeNode biTree)
	{//中序遍歷費遞迴實現
		Stack<TreeNode> stack = new Stack<TreeNode>();
		while(biTree != null || !stack.isEmpty())
		{
			while(biTree != null)
			{
				stack.push(biTree);
				biTree = biTree.left;
			}
			if(!stack.isEmpty())
			{
				biTree = stack.pop();
				System.out.println(biTree.value);
				biTree = biTree.right;
			}
		}
	}
}

class TreeNode//節點結構
{
	int value;
	TreeNode left;
	TreeNode right;
	
	TreeNode(int value)
	{
		this.value = value;
	}
}




3.後序遍歷(難點)

後序遍歷(LRD)是二叉樹遍歷的一種,也叫做後根遍歷、後序周遊,可記做左右根。後序遍歷有遞迴演算法和非遞迴演算法兩種。在二叉樹中,先左後右再根。巧記:左右根。後序遍歷首先遍歷左子樹,然後遍歷右子樹,最後訪問根結點,在遍歷左、右子樹時,仍然先遍歷左子樹,然後遍歷右子樹,最後遍歷根結點。即:若二叉樹為空則結束返回,否則:(1)後序遍歷左子樹(2)後序遍歷右子樹(3)訪問根結點如右圖所示二叉樹後序遍歷結果:DEBFCA已知前序遍歷和中序遍歷,就能確定後序遍歷。演算法核心思想:
    首先要搞清楚先序、中序、後序的非遞迴演算法共同之處:用棧來儲存先前走過的路徑,以便可以在訪問完子樹後,可以利用棧中的資訊,回退到當前節點的雙親節點,進行下一步操作。
    後序遍歷的非遞迴演算法是三種順序中最複雜的,原因在於,後序遍歷是先訪問左、右子樹,再訪問根節點,而在非遞迴演算法中,利用棧回退到時,並不知道是從左子樹回退到根節點,還是從右子樹回退到根節點,如果從左子樹回退到根節點,此時就應該去訪問右子樹,而如果從右子樹回退到根節點,此時就應該訪問根節點。所以相比前序和後序,必須得在壓棧時新增資訊,以便在退棧時可以知道是從左子樹返回,還是從右子樹返回進而決定下一步的操作。
import java.util.Stack;
public class Test 
{
	public static void main(String[] args)
	{
		TreeNode[] node = new TreeNode[10];//以陣列形式生成一棵完全二叉樹
		for(int i = 0; i < 10; i++)
		{
			node[i] = new TreeNode(i);
		}
		for(int i = 0; i < 10; i++)
		{
			if(i*2+1 < 10)
				node[i].left = node[i*2+1];
			if(i*2+2 < 10)
				node[i].right = node[i*2+2];
		}
		
		postOrderRe(node[0]);
		System.out.println("***");
		postOrder(node[0]);
	}
	
	
	
	public static void postOrderRe(TreeNode biTree)
	{//後序遍歷遞迴實現
		if(biTree == null)
			return;
		else
		{
			postOrderRe(biTree.left);
			postOrderRe(biTree.right);
			System.out.println(biTree.value);
		}
	}
	
	public static void postOrder(TreeNode biTree)
	{//後序遍歷非遞迴實現
		int left = 1;//在輔助棧裡表示左節點
		int right = 2;//在輔助棧裡表示右節點
		Stack<TreeNode> stack = new Stack<TreeNode>();
		Stack<Integer> stack2 = new Stack<Integer>();//輔助棧,用來判斷子節點返回父節點時處於左節點還是右節點。
		
		while(biTree != null || !stack.empty())
		{
			while(biTree != null)
			{//將節點壓入棧1,並在棧2將節點標記為左節點
				stack.push(biTree);
				stack2.push(left);
				biTree = biTree.left;
			}
			
			while(!stack.empty() && stack2.peek() == right)
			{//如果是從右子節點返回父節點,則任務完成,將兩個棧的棧頂彈出
				stack2.pop();
				System.out.println(stack.pop().value);
			}
			
			if(!stack.empty() && stack2.peek() == left)
			{//如果是從左子節點返回父節點,則將標記改為右子節點
				stack2.pop();
				stack2.push(right);
				biTree = stack.peek().right;
			}
				
		}
	}
}

class TreeNode//節點結構
{
	int value;
	TreeNode left;
	TreeNode right;
	
	TreeNode(int value)
	{
		this.value = value;
	}
}


4.層次遍歷

    與樹的前中後序遍歷的DFS思想不同,層次遍歷用到的是BFS思想。一般DFS用遞迴去實現(也可以用棧實現),BFS需要用佇列去實現。
層次遍歷的步驟是:
    1.對於不為空的結點,先把該結點加入到佇列中
    2.從隊中拿出結點,如果該結點的左右結點不為空,就分別把左右結點加入到佇列中

    3.重複以上操作直到佇列為空

	public static void levelOrder(TreeNode biTree)
	{//層次遍歷
		if(biTree == null)
			return;
		LinkedList<TreeNode> list = new LinkedList<TreeNode>();
		list.add(biTree);
		TreeNode currentNode;
		while(!list.isEmpty())
		{
			currentNode = list.poll();
			System.out.println(currentNode.value);
			if(currentNode.left != null)
				list.add(currentNode.left);
			if(currentNode.right != null)
				list.add(currentNode.right);
		}
	}
先序遍歷特點:第一個值是根節點
中序遍歷特點:根節點左邊都是左子樹,右邊都是右子樹