1. 程式人生 > >面試大總結之二:Java搞定面試中的二叉樹題目

面試大總結之二:Java搞定面試中的二叉樹題目

這是本系列的第二篇,與前一篇 面試大總結之一:Java搞定面試中的連結串列題目 相比,二叉樹的題目可以變化的就更多了。本文還是參考整合重寫了《輕鬆搞定面試中的二叉樹題目》和《演算法大全(3) 二叉樹》兩篇大作。本文一個小亮點就是幾乎每一道題都用了遞迴和迭代兩種方法寫過一遍,因為面試時往往可能會被要求寫不擅長的那一種。這一千多行的記錄也是我在面試摸索過程中的一個小筆記,備份與此。請大神們輕拍指正。今後我也會不斷更新加入新題新方法。

package BinaryTreeSummary;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;

/**
 * http://blog.csdn.net/luckyxiaoqiang/article/details/7518888  輕鬆搞定面試中的二叉樹題目
 * http://www.cnblogs.com/Jax/archive/2009/12/28/1633691.html  演算法大全(3) 二叉樹
 * 
 * TODO: 一定要能熟練地寫出所有問題的遞迴和非遞迴做法!
 *
 * 1. 求二叉樹中的節點個數: getNodeNumRec(遞迴),getNodeNum(迭代)
 * 2. 求二叉樹的深度: getDepthRec(遞迴),getDepth 
 * 3. 前序遍歷,中序遍歷,後序遍歷: preorderTraversalRec, preorderTraversal, inorderTraversalRec, postorderTraversalRec
 * (https://en.wikipedia.org/wiki/Tree_traversal#Pre-order_2)
 * 4.分層遍歷二叉樹(按層次從上往下,從左往右): levelTraversal, levelTraversalRec(遞迴解法!)
 * 5. 將二叉查詢樹變為有序的雙向連結串列: convertBST2DLLRec, convertBST2DLL
 * 6. 求二叉樹第K層的節點個數:getNodeNumKthLevelRec, getNodeNumKthLevel
 * 7. 求二叉樹中葉子節點的個數:getNodeNumLeafRec, getNodeNumLeaf
 * 8. 判斷兩棵二叉樹是否相同的樹:isSameRec, isSame
 * 9. 判斷二叉樹是不是平衡二叉樹:isAVLRec
 * 10. 求二叉樹的映象(破壞和不破壞原來的樹兩種情況):mirrorRec, mirrorCopyRec
 * 10.1 判斷兩個樹是否互相映象:isMirrorRec
 * 11. 求二叉樹中兩個節點的最低公共祖先節點:getLastCommonParent, getLastCommonParentRec, getLastCommonParentRec2
 * 12. 求二叉樹中節點的最大距離:getMaxDistanceRec
 * 13. 由前序遍歷序列和中序遍歷序列重建二叉樹:rebuildBinaryTreeRec
 * 14.判斷二叉樹是不是完全二叉樹:isCompleteBinaryTree, isCompleteBinaryTreeRec
 * 
 */
public class Demo {

	/*
	 			 1 
		        / \ 
		       2   3 
		      / \   \ 
		     4  5   6 
	 */
	public static void main(String[] args) {
		TreeNode r1 = new TreeNode(1);
		TreeNode r2 = new TreeNode(2);
		TreeNode r3 = new TreeNode(3);
		TreeNode r4 = new TreeNode(4);
		TreeNode r5 = new TreeNode(5);
		TreeNode r6 = new TreeNode(6);
		
		r1.left = r2;
		r1.right = r3;
		r2.left = r4;
		r2.right = r5;
		r3.right = r6;
		
//		System.out.println(getNodeNumRec(r1));
//		System.out.println(getNodeNum(r1));
//		System.out.println(getDepthRec(r1));
//		System.out.println(getDepth(r1));
		
//		preorderTraversalRec(r1);
//		System.out.println();
//		preorderTraversal(r1);
//		System.out.println();
//		inorderTraversalRec(r1);
//		System.out.println();
//		inorderTraversal(r1);
//		System.out.println();
//		postorderTraversalRec(r1);
//		System.out.println();
//		postorderTraversal(r1);
//		System.out.println();
//		levelTraversal(r1);
//		System.out.println();
//		levelTraversalRec(r1);
//		System.out.println();
		
//		TreeNode tmp = convertBSTRec(r1);
//		while(true){
//			if(tmp == null){
//				break;
//			}
//			System.out.print(tmp.val + " ");
//			if(tmp.right == null){
//				break;
//			}
//			tmp = tmp.right;
//		}
//		System.out.println();
//		while(true){
//			if(tmp == null){
//				break;
//			}
//			System.out.print(tmp.val + " ");
//			if(tmp.left == null){
//				break;
//			}
//			tmp = tmp.left;
//		}
		
		
//		TreeNode tmp = convertBST2DLL(r1);
//		while(true){
//			if(tmp == null){
//				break;
//			}
//			System.out.print(tmp.val + " ");
//			if(tmp.right == null){
//				break;
//			}
//			tmp = tmp.right;
//		}
		
//		System.out.println(getNodeNumKthLevelRec(r1, 2));
//		System.out.println(getNodeNumKthLevel(r1, 2));
		
//		System.out.println(getNodeNumLeafRec(r1));
//		System.out.println(getNodeNumLeaf(r1));
		
//		System.out.println(isSame(r1, r1));
//		inorderTraversal(r1);
//		System.out.println();
//		mirror(r1);
//		TreeNode mirrorRoot = mirrorCopy(r1);
//		inorderTraversal(mirrorRoot);
		
		System.out.println(isCompleteBinaryTree(r1));
		System.out.println(isCompleteBinaryTreeRec(r1));
		
	}

	private static class TreeNode {
		int val;
		TreeNode left;
		TreeNode right;

		public TreeNode(int val) {
			this.val = val;
		}
	}

	/**
	 * 求二叉樹中的節點個數遞迴解法: O(n)
	 * (1)如果二叉樹為空,節點個數為0 
	 * (2)如果二叉樹不為空,二叉樹節點個數 = 左子樹節點個數 +
	 *    	      右子樹節點個數 + 1
	 */
	public static int getNodeNumRec(TreeNode root) {
		if (root == null) {
			return 0;
		} else {
			return getNodeNumRec(root.left) + getNodeNumRec(root.right) + 1;
		}
	}
	
	/**
	 *  求二叉樹中的節點個數迭代解法O(n):基本思想同LevelOrderTraversal,
	 *  即用一個Queue,在Java裡面可以用LinkedList來模擬 
	 */
	public static int getNodeNum(TreeNode root) {
		if(root == null){
			return 0;
		}
		int count = 1;
		Queue<TreeNode> queue = new LinkedList<TreeNode>();
		queue.add(root);
		
		while(!queue.isEmpty()){
			TreeNode cur = queue.remove();		// 從隊頭位置移除
			if(cur.left != null){			// 如果有左孩子,加到隊尾
				queue.add(cur.left);
				count++;
			}
			if(cur.right != null){		// 如果有右孩子,加到隊尾
				queue.add(cur.right);
				count++;
			}
		}
		
		return count;
	}

	/**
	 * 求二叉樹的深度(高度) 遞迴解法: O(n)
	 * (1)如果二叉樹為空,二叉樹的深度為0 
	 * (2)如果二叉樹不為空,二叉樹的深度 = max(左子樹深度, 右子樹深度) + 1
	 */
	public static int getDepthRec(TreeNode root) {
		if (root == null) {
			return 0;
		}

		int leftDepth = getDepthRec(root.left);
		int rightDepth = getDepthRec(root.right);
		return Math.max(leftDepth, rightDepth) + 1;
	}
	
	/**
	 * 求二叉樹的深度(高度) 迭代解法: O(n)
	 * 基本思想同LevelOrderTraversal,還是用一個Queue
	 */
	public static int getDepth(TreeNode root) {
		if(root == null){
			return 0;
		}
		
		int depth = 0;							// 深度
		int currentLevelNodes = 1;		// 當前Level,node的數量
		int nextLevelNodes = 0;			// 下一層Level,node的數量
		
		LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
		queue.add(root);
		
		while( !queue.isEmpty() ){
			TreeNode cur = queue.remove();		// 從隊頭位置移除
			currentLevelNodes--;			// 減少當前Level node的數量
			if(cur.left != null){				// 如果有左孩子,加到隊尾
				queue.add(cur.left);
				nextLevelNodes++;			// 並增加下一層Level node的數量
			}
			if(cur.right != null){			// 如果有右孩子,加到隊尾
				queue.add(cur.right);
				nextLevelNodes++;
			}
			
			if(currentLevelNodes == 0){ // 說明已經遍歷完當前層的所有節點
				depth++;					   // 增加高度
				currentLevelNodes = nextLevelNodes;		// 初始化下一層的遍歷
				nextLevelNodes = 0;
			}
		}
		
		return depth;
	}
	
	

	/**
	 * 前序遍歷,中序遍歷,後序遍歷 前序遍歷遞迴解法: 
	 * (1)如果二叉樹為空,空操作 
	 * (2)如果二叉樹不為空,訪問根節點,前序遍歷左子樹,前序遍歷右子樹
	 */
	public static void preorderTraversalRec(TreeNode root) {
		if (root == null) {
			return;
		}
		System.out.print(root.val + " ");
		preorderTraversalRec(root.left);
		preorderTraversalRec(root.right);
	}
	
	/**
	 *  前序遍歷迭代解法:用一個輔助stack,總是把右孩子放進棧
	 *  http://www.youtube.com/watch?v=uPTCbdHSFg4
	 */
	public static void preorderTraversal(TreeNode root) {
		if(root == null){
			return;
		}
		
		Stack<TreeNode> stack = new Stack<TreeNode>();		// 輔助stack
		stack.push(root);
		
		while( !stack.isEmpty() ){
			TreeNode cur = stack.pop();		// 出棧棧頂元素
			System.out.print(cur.val + " ");
			
			// 關鍵點:要先壓入右孩子,再壓入左孩子,這樣在出棧時會先列印左孩子再列印右孩子
			if(cur.right != null){
				stack.push(cur.right);
			}
			if(cur.left != null){
				stack.push(cur.left);
			}
		}
	}

	/**
	 * 中序遍歷遞迴解法 
	 * (1)如果二叉樹為空,空操作。 
	 * (2)如果二叉樹不為空,中序遍歷左子樹,訪問根節點,中序遍歷右子樹
	 */
	public static void inorderTraversalRec(TreeNode root) {
		if (root == null) {
			return;
		}
		inorderTraversalRec(root.left);
		System.out.print(root.val + " ");
		inorderTraversalRec(root.right);
	}
	
	/**
	 * 中序遍歷迭代解法 ,用棧先把根節點的所有左孩子都新增到棧內,
	 * 然後輸出棧頂元素,再處理棧頂元素的右子樹
	 * http://www.youtube.com/watch?v=50v1sJkjxoc
	 * 
	 * 還有一種方法能不用遞迴和棧,基於線索二叉樹的方法,較麻煩以後補上
	 * http://www.geeksforgeeks.org/inorder-tree-traversal-without-recursion-and-without-stack/
	 */
	public static void inorderTraversal(TreeNode root){
		if(root == null){
			return;
		}
		Stack<TreeNode> stack = new Stack<TreeNode>();
		TreeNode cur = root;
		
		while( true ){
			while(cur != null){		// 先新增一個非空節點所有的左孩子到棧
				stack.push(cur);
				cur = cur.left;
			}
			
			if(stack.isEmpty()){
				break;
			}
				
			// 因為此時已經沒有左孩子了,所以輸出棧頂元素
			cur = stack.pop();
			System.out.print(cur.val + " ");
			cur = cur.right;	// 準備處理右子樹
		}
	}

	/**
	 * 後序遍歷遞迴解法 
	 * (1)如果二叉樹為空,空操作 
	 * (2)如果二叉樹不為空,後序遍歷左子樹,後序遍歷右子樹,訪問根節點
	 */
	public static void postorderTraversalRec(TreeNode root) {
		if (root == null) {
			return;
		}
		postorderTraversalRec(root.left);
		postorderTraversalRec(root.right);
		System.out.print(root.val + " ");
	}
	
	/**
	 *  後序遍歷迭代解法
	 *  http://www.youtube.com/watch?v=hv-mJUs5mvU
	 *  
	 */
	public static void postorderTraversal(TreeNode root) {
		if (root == null) {
			return;
		}
		
		Stack<TreeNode> s = new Stack<TreeNode>();		// 第一個stack用於新增node和它的左右孩子
		Stack<TreeNode> output = new Stack<TreeNode>();// 第二個stack用於翻轉第一個stack輸出
		
		s.push(root);
		while( !s.isEmpty() ){		// 確保所有元素都被翻轉轉移到第二個stack
			TreeNode cur = s.pop();	// 把棧頂元素新增到第二個stack
			output.push(cur);		
			
			if(cur.left != null){		// 把棧頂元素的左孩子和右孩子分別新增入第一個stack
				s.push(cur.left);
			}
			if(cur.right != null){
				s.push(cur.right);
			}
		}
		
		while( !output.isEmpty() ){	// 遍歷輸出第二個stack,即為後序遍歷
			System.out.print(output.pop().val + " ");
		}
	}

	/**
	 * 分層遍歷二叉樹(按層次從上往下,從左往右)迭代
	 * 相當於廣度優先搜尋,使用佇列實現。佇列初始化,將根節點壓入佇列。當佇列不為空,進行如下操作:彈出一個節點
	 * ,訪問,若左子節點或右子節點不為空,將其壓入佇列
	 */
	public static void levelTraversal(TreeNode root) {
		if (root == null) {
			return;
		}
		LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
		queue.push(root);

		while (!queue.isEmpty()) {
			TreeNode cur = queue.removeFirst();
			System.out.print(cur.val + " ");
			if (cur.left != null) {
				queue.add(cur.left);
			}
			if (cur.right != null) {
				queue.add(cur.right);
			}
		}
	}
	
	/**
	 *  分層遍歷二叉樹(遞迴)
	 *  很少有人會用遞迴去做level traversal
	 *  基本思想是用一個大的ArrayList,裡面包含了每一層的ArrayList。
	 *  大的ArrayList的size和level有關係
	 *  
	 *  這是我目前見到的最好的遞迴解法!
	 *  http://discuss.leetcode.com/questions/49/binary-tree-level-order-traversal#answer-container-2543
	 */
	public static void levelTraversalRec(TreeNode root) {
		ArrayList<ArrayList<Integer>> ret = new ArrayList<ArrayList<Integer>>();
		dfs(root, 0, ret);
		System.out.println(ret);
	}
	
	private static void dfs(TreeNode root, int level, ArrayList<ArrayList<Integer>> ret){
		if(root == null){
			return;
		}
		
		// 新增一個新的ArrayList表示新的一層
		if(level >= ret.size()){
			ret.add(new ArrayList<Integer>());
		}
		
		ret.get(level).add(root.val);	// 把節點新增到表示那一層的ArrayList裡
		dfs(root.left, level+1, ret);		// 遞迴處理下一層的左子樹和右子樹
		dfs(root.right, level+1, ret);
	}
	

	/**
	 * 將二叉查詢樹變為有序的雙向連結串列 要求不能建立新節點,只調整指標。 
	 * 遞迴解法:
	 * 參考了http://stackoverflow.com/questions/11511898/converting-a-binary-search-tree-to-doubly-linked-list#answer-11530016
	 * 感覺是最清晰的遞迴解法,但要注意遞迴完,root會在連結串列的中間位置,因此要手動
	 * 把root移到連結串列頭或連結串列尾
	 */
	public static TreeNode convertBST2DLLRec(TreeNode root) {
		root = convertBST2DLLSubRec(root);
		
		// root會在連結串列的中間位置,因此要手動把root移到連結串列頭
		while(root.left != null){
			root = root.left;
		}
		return root;
	}
	
	/**
	 *  遞迴轉換BST為雙向連結串列(DLL)
	 */
	public static TreeNode convertBST2DLLSubRec(TreeNode root){
		if(root==null || (root.left==null && root.right==null)){
			return root;
		}
		
		TreeNode tmp = null;
		if(root.left != null){			// 處理左子樹
			tmp = convertBST2DLLSubRec(root.left);
			while(tmp.right != null){	// 尋找最右節點
				tmp = tmp.right;
			}
			tmp.right = root;		// 把左子樹處理後結果和root連線
			root.left = tmp;
		}
		if(root.right != null){		// 處理右子樹
			tmp = convertBST2DLLSubRec(root.right);
			while(tmp.left != null){	// 尋找最左節點
				tmp = tmp.left;
			}
			tmp.left = root;		// 把右子樹處理後結果和root連線
			root.right = tmp;
		}
		return root;
	}
	
	/**
	 * 將二叉查詢樹變為有序的雙向連結串列 迭代解法
//	 * 類似inorder traversal的做法
	 */
	public static TreeNode convertBST2DLL(TreeNode root) {
		if(root == null){
			return null;
		}
		Stack<TreeNode> stack = new Stack<TreeNode>();
		TreeNode cur = root;		// 指向當前處理節點
		TreeNode old = null;			// 指向前一個處理的節點
		TreeNode head = null;		// 連結串列頭
		
		while( true ){
			while(cur != null){		// 先新增一個非空節點所有的左孩子到棧
				stack.push(cur);
				cur = cur.left;
			}
			
			if(stack.isEmpty()){
				break;
			}
				
			// 因為此時已經沒有左孩子了,所以輸出棧頂元素
			cur = stack.pop();
			if(old != null){
				old.right = cur;
			}
			if(head == null){		// /第一個節點為雙向連結串列頭節點
				head = cur;
			}
			
			old = cur;			// 更新old
			cur = cur.right;	// 準備處理右子樹
		}
		
		return head;
	}

	/**
	 * 求二叉樹第K層的節點個數   遞迴解法: 
	 * (1)如果二叉樹為空或者k<1返回0
	 * (2)如果二叉樹不為空並且k==1,返回1
	 * (3)如果二叉樹不為空且k>1,返回root左子樹中k-1層的節點個數與root右子樹k-1層節點個數之和
	 * 
	 * 求以root為根的k層節點數目 等價於 求以root左孩子為根的k-1層(因為少了root那一層)節點數目 加上
	 * 以root右孩子為根的k-1層(因為少了root那一層)節點數目
	 * 
	 * 所以遇到樹,先把它拆成左子樹和右子樹,把問題降解
	 * 
	 */
	public static int getNodeNumKthLevelRec(TreeNode root, int k) {
		if (root == null || k < 1) {
			return 0;
		}

		if (k == 1) {
			return 1;
		}
		int numLeft = getNodeNumKthLevelRec(root.left, k - 1); 		// 求root左子樹的k-1層節點數
		int numRight = getNodeNumKthLevelRec(root.right, k - 1); 	// 求root右子樹的k-1層節點數
		return numLeft + numRight;
	}
	
	/**
	 *  求二叉樹第K層的節點個數   迭代解法: 
	 *  同getDepth的迭代解法
	 */
	public static int getNodeNumKthLevel(TreeNode root, int k){
		if(root == null){
			return 0;
		}
		Queue<TreeNode> queue = new LinkedList<TreeNode>();
		queue.add(root);
		
		int i = 1;
		int currentLevelNodes = 1;		// 當前Level,node的數量
		int nextLevelNodes = 0;			// 下一層Level,node的數量
		
		while( !queue.isEmpty() && i<k){
			TreeNode cur = queue.remove();		// 從隊頭位置移除
			currentLevelNodes--;			// 減少當前Level node的數量
			if(cur.left != null){				// 如果有左孩子,加到隊尾
				queue.add(cur.left);
				nextLevelNodes++;			// 並增加下一層Level node的數量
			}
			if(cur.right != null){			// 如果有右孩子,加到隊尾
				queue.add(cur.right);
				nextLevelNodes++;
			}
			
			if(currentLevelNodes == 0){ // 說明已經遍歷完當前層的所有節點
				currentLevelNodes = nextLevelNodes;		// 初始化下一層的遍歷
				nextLevelNodes = 0;
				i++;			// 進入到下一層
			}
		}
		
		return currentLevelNodes;
	}

	/**
	 * 求二叉樹中葉子節點的個數(遞迴)
	 */
	public static int getNodeNumLeafRec(TreeNode root) {
		// 當root不存在,返回空
		if (root == null) {
			return 0;
		}

		// 當為葉子節點時返回1
		if (root.left == null && root.right == null) {
			return 1;
		}

		// 把一個樹拆成左子樹和右子樹之和,原理同上一題
		return getNodeNumLeafRec(root.left) + getNodeNumLeafRec(root.right);
	}
	
	/**
	 *  求二叉樹中葉子節點的個數(迭代)
	 *  還是基於Level order traversal
	 */
	public static int getNodeNumLeaf(TreeNode root) {
		if(root == null){
			return 0;
		}
		Queue<TreeNode> queue = new LinkedList<TreeNode>();
		queue.add(root);
		
		int leafNodes = 0;				// 記錄上一個Level,node的數量
		
		while( !queue.isEmpty() ){
			TreeNode cur = queue.remove();		// 從隊頭位置移除
			if(cur.left != null){				// 如果有左孩子,加到隊尾
				queue.add(cur.left);
			}
			if(cur.right != null){				// 如果有右孩子,加到隊尾
				queue.add(cur.right);
			}
			if(cur.left==null && cur.right==null){			// 葉子節點
				leafNodes++;
			}
		}
		
		return leafNodes;
	}

	/**
	 * 判斷兩棵二叉樹是否相同的樹。
	 * 遞迴解法: 
	 * (1)如果兩棵二叉樹都為空,返回真
	 * (2)如果兩棵二叉樹一棵為空,另一棵不為空,返回假 
	 * (3)如果兩棵二叉樹都不為空,如果對應的左子樹和右子樹都同構返回真,其他返回假
	 */
	public static boolean isSameRec(TreeNode r1, TreeNode r2) {
		// 如果兩棵二叉樹都為空,返回真
		if (r1 == null && r2 == null) {
			return true;
		}
		// 如果兩棵二叉樹一棵為空,另一棵不為空,返回假
		else if (r1 == null || r2 == null) {
			return false;
		}

		if(r1.val != r2.val){
			return false;
		}
		boolean leftRes = isSameRec(r1.left, r2.left); 		// 比較對應左子樹
		boolean rightRes = isSameRec(r1.right, r2.right); // 比較對應右子樹
		return leftRes && rightRes;
	}
	
	/**
	 * 判斷兩棵二叉樹是否相同的樹(迭代)
	 * 遍歷一遍即可,這裡用preorder
	 */
	public static boolean isSame(TreeNode r1, TreeNode r2) {
		// 如果兩個樹都是空樹,則返回true
		if(r1==null && r2==null){
			return true;
		}
		
		// 如果有一棵樹是空樹,另一顆不是,則返回false
		if(r1==null || r2==null){
			return false;
		}
		
		Stack<TreeNode> s1 = new Stack<TreeNode>();
		Stack<TreeNode> s2 = new Stack<TreeNode>();
		
		s1.push(r1);
		s2.push(r2);
		
		while(!s1.isEmpty() && !s2.isEmpty()){
			TreeNode n1 = s1.pop();
			TreeNode n2 = s2.pop();
			if(n1==null && n2==null){
				continue;
			}else if(n1!=null && n2!=null && n1.val==n2.val){
				s1.push(n1.right);
				s1.push(n1.left);
				s2.push(n2.right);
				s2.push(n2.left);
			}else{
				return false;
			}
		}
		return true;
	}

	/**
	 * 判斷二叉樹是不是平衡二叉樹 遞迴解法: 
	 * (1)如果二叉樹為空,返回真
	 * (2)如果二叉樹不為空,如果左子樹和右子樹都是AVL樹並且左子樹和右子樹高度相差不大於1,返回真,其他返回假
	 */
	public static boolean isAVLRec(TreeNode root) {
		if(root == null){			// 如果二叉樹為空,返回真
			return true;
		}
		
		// 如果左子樹和右子樹高度相差大於1,則非平衡二叉樹, getDepthRec()是前面實現過的求樹高度的方法
		if(Math.abs(getDepthRec(root.left) - getDepthRec(root.right)) > 1){
			return false;
		}
		
		// 遞迴判斷左子樹和右子樹是否為平衡二叉樹
		return isAVLRec(root.left) && isAVLRec(root.right);
	}
	

	/**
	 * 求二叉樹的映象 遞迴解法: 
	 * (1)如果二叉樹為空,返回空
	 * (2)如果二叉樹不為空,求左子樹和右子樹的映象,然後交換左子樹和右子樹
	 */
	// 1. 破壞原來的樹,把原來的樹改成其映象
	public static TreeNode mirrorRec(TreeNode root) {
		if (root == null) {
			return null;
		}

		TreeNode left = mirrorRec(root.left);
		TreeNode right = mirrorRec(root.right);

		root.left = right;
		root.right = left;
		return root;
	}
	
	// 2. 不能破壞原來的樹,返回一個新的映象樹
	public static TreeNode mirrorCopyRec(TreeNode root){
		if(root == null){
			return null;
		}
		
		TreeNode newNode = new TreeNode(root.val);
		newNode.left = mirrorCopyRec(root.right);
		newNode.right = mirrorCopyRec(root.left);
		
		return newNode;
	}
	
	// 3. 判斷兩個樹是否互相映象
	public static boolean isMirrorRec(TreeNode r1, TreeNode r2){
		// 如果兩個樹都是空樹,則返回true
		if(r1==null && r2==null){
			return true;
		}
		
		// 如果有一棵樹是空樹,另一顆不是,則返回false
		if(r1==null || r2==null){
			return false;
		}
		
		// 如果兩個樹都非空樹,則先比較根節點
		if(r1.val != r2.val){
			return false;
		}
		
		// 遞迴比較r1的左子樹的映象是不是r2右子樹 和 
		// r1的右子樹的映象是不是r2左子樹
		return isMirrorRec(r1.left, r2.right) && isMirrorRec(r1.right, r2.left);
	}
	
	// 1. 破壞原來的樹,把原來的樹改成其映象
	public static void mirror(TreeNode root) {
		if(root == null){
			return;
		}
		
		Stack<TreeNode> stack = new Stack<TreeNode>();
		stack.push(root);
		while( !stack.isEmpty() ){
			TreeNode cur = stack.pop();
			
			// 交換左右孩子
			TreeNode tmp = cur.right;
			cur.right = cur.left;
			cur.left = tmp;
			
			if(cur.right != null){
				stack.push(cur.right);
			}
			if(cur.left != null){
				stack.push(cur.left);
			}
		}
	}
	
	// 2. 不能破壞原來的樹,返回一個新的映象樹
	public static TreeNode mirrorCopy(TreeNode root){
		if(root == null){
			return null;
		}
		
		Stack<TreeNode> stack = new Stack<TreeNode>();
		Stack<TreeNode> newStack = new Stack<TreeNode>();
		stack.push(root);
		TreeNode newRoot = new TreeNode(root.val);
		newStack.push(newRoot);
		
		while( !stack.isEmpty() ){
			TreeNode cur = stack.pop();
			TreeNode newCur = newStack.pop();
			
			if(cur.right != null){
				stack.push(cur.right);
				newCur.left = new TreeNode(cur.right.val);
				newStack.push(newCur.left);
			}
			if(cur.left != null){
				stack.push(cur.left);
				newCur.right = new TreeNode(cur.left.val);
				newStack.push(newCur.right);
			}
		}
		
		return newRoot;
	}
	

	/**
	 * 求二叉樹中兩個節點的最低公共祖先節點 
	 * 遞迴解法: 
	 * (1)如果兩個節點分別在根節點的左子樹和右子樹,則返回根節點
	 * (2)如果兩個節點都在左子樹,則遞迴處理左子樹;如果兩個節點都在右子樹,則遞迴處理右子樹
	 */
	public static TreeNode getLastCommonParentRec(TreeNode root, TreeNode n1, TreeNode n2) {
		if (findNodeRec(root.left, n1)) {				// 如果n1在樹的左子樹
			if (findNodeRec(root.right, n2)) { 		// 如果n2在樹的右子樹
				return root; 								// 返回根節點
			} else { 			// 如果n2也在樹的左子樹
				return getLastCommonParentRec(root.left, n1, n2); // 遞迴處理
			}
		} else { 				// 如果n1在樹的右子樹
			if (findNodeRec(root.left, n2)) { 			// 如果n2在左子樹
				return root;
			} else {				 // 如果n2在右子樹
				return getLastCommonParentRec(root.right, n1, n2); // 遞迴處理
			}
		}
	}

	// 幫助方法,遞迴判斷一個點是否在樹裡
	private static boolean findNodeRec(TreeNode root, TreeNode node) {
		if (root == null || node == null) {
			return false;
		}
		if (root == node) {
			return true;
		}

		// 先嚐試在左子樹中查詢
		boolean found = findNodeRec(root.left, node);
		if (!found) { 		// 如果查詢不到,再在右子樹中查詢
			found = findNodeRec(root.right, node);
		}
		return found;
	}
	
	// 求二叉樹中兩個節點的最低公共祖先節點 (更加簡潔版的遞迴)
	public static TreeNode getLastCommonParentRec2(TreeNode root, TreeNode n1, TreeNode n2) {
		if(root == null){
			return null;
		}
		
		// 如果有一個match,則說明當前node就是要找的最低公共祖先
		if(root.equals(n1) || root.equals(n2)){
			return root;
		}
		TreeNode commonInLeft = getLastCommonParentRec2(root.left, n1, n2);
		TreeNode commonInRight = getLastCommonParentRec2(root.right, n1, n2);
		
		// 如果一個左子樹找到,一個在右子樹找到,則說明root是唯一可能的最低公共祖先
		if(commonInLeft!=null && commonInRight!=null){
			return root;
		}
		
		// 其他情況是要不然在左子樹要不然在右子樹
		if(commonInLeft != null){
			return commonInLeft;
		}
		
		return commonInRight;
	}

	/**
	 * 非遞迴解法: 
	 * 先求從根節點到兩個節點的路徑,然後再比較對應路徑的節點就行,最後一個相同的節點也就是他們在二叉樹中的最低公共祖先節點
	 */
	public static TreeNode getLastCommonParent(TreeNode root, TreeNode n1, TreeNode n2) {
		if (root == null || n1 == null || n2 == null) {
			return null;
		}

		ArrayList<TreeNode> p1 = new ArrayList<TreeNode>();
		boolean res1 = getNodePath(root, n1, p1);
		ArrayList<TreeNode> p2 = new ArrayList<TreeNode>();
		boolean res2 = getNodePath(root, n2, p2);

		if (!res1 || !res2) {
			return null;
		}

		TreeNode last = null;
		Iterator<TreeNode> iter1 = p1.iterator();
		Iterator<TreeNode> iter2 = p2.iterator();

		while (iter1.hasNext() && iter2.hasNext()) {
			TreeNode tmp1 = iter1.next();
			TreeNode tmp2 = iter2.next();
			if (tmp1 == tmp2) {
				last = tmp1;
			} else { // 直到遇到非公共節點
				break;
			}
		}
		return last;
	}

	// 把從根節點到node路徑上所有的點都新增到path中
	private static boolean getNodePath(TreeNode root, TreeNode node, ArrayList<TreeNode> path) {
		if (root == null) {
			return false;
		}
		
		path.add(root);		// 把這個節點加到路徑中
		if (root == node) {
			return true;
		}

		boolean found = false;
		found = getNodePath(root.left, node, path); // 先在左子樹中找
		
		if (!found) { 				// 如果沒找到,再在右子樹找
			found = getNodePath(root.right, node, path);
		}
		if (!found) { 				// 如果實在沒找到證明這個節點不在路徑中,說明剛才新增進去的不是路徑上的節點,刪掉!
			path.remove(root);	
		}

		return found;
	}

	/**
	 * 求二叉樹中節點的最大距離 即二叉樹中相距最遠的兩個節點之間的距離。 (distance / diameter)
	 * 遞迴解法: 
	 * (1)如果二叉樹為空,返回0,同時記錄左子樹和右子樹的深度,都為0
	 * (2)如果二叉樹不為空,最大距離要麼是左子樹中的最大距離,要麼是右子樹中的最大距離,
	 * 要麼是左子樹節點中到根節點的最大距離+右子樹節點中到根節點的最大距離,
	 * 同時記錄左子樹和右子樹節點中到根節點的最大距離。
	 * 
	 * http://www.cnblogs.com/miloyip/archive/2010/02/25/1673114.html
	 * 
	 * 計算一個二叉樹的最大距離有兩個情況:

		情況A: 路徑經過左子樹的最深節點,通過根節點,再到右子樹的最深節點。
		情況B: 路徑不穿過根節點,而是左子樹或右子樹的最大距離路徑,取其大者。
		只需要計算這兩個情況的路徑距離,並取其大者,就是該二叉樹的最大距離
	 */
	public static Result getMaxDistanceRec(TreeNode root){
		if(root == null){
			Result empty = new Result(0, -1);		// 目的是讓呼叫方 +1 後,把當前的不存在的 (NULL) 子樹當成最大深度為 0
			return empty;
		}
		
		// 計算出左右子樹分別最大距離
		Result lmd = getMaxDistanceRec(root.left);
		Result rmd = getMaxDistanceRec(root.right);
		
		Result res = new Result();
		res.maxDepth = Math.max(lmd.maxDepth, rmd.maxDepth) + 1;		// 當前最大深度
		// 取情況A和情況B中較大值
		res.maxDistance = Math.max( lmd.maxDepth+rmd.maxDepth, Math.max(lmd.maxDistance, rmd.maxDistance) );
		return res;
	}
	
	private static class Result{
		int maxDistance;
		int maxDepth;
		public Result() {
		}

		public Result(int maxDistance, int maxDepth) {
			this.maxDistance = maxDistance;
			this.maxDepth = maxDepth;
		}
	}
	
	/**
	 * 13. 由前序遍歷序列和中序遍歷序列重建二叉樹(遞迴)
	 * 感覺這篇是講的最為清晰的:
	 * http://crackinterviewtoday.wordpress.com/2010/03/15/rebuild-a-binary-tree-from-inorder-and-preorder-traversals/
	 * 文中還提到一種避免開額外空間的方法,等下次補上
	 */
	public static TreeNode rebuildBinaryTreeRec(List<Integer> preOrder, List<Integer> inOrder){
		TreeNode root = null;
        List<Integer> leftPreOrder;
        List<Integer> rightPreOrder;
        List<Integer> leftInorder;
        List<Integer> rightInorder;
        int inorderPos;
        int preorderPos;
 
        if ((preOrder.size() != 0) && (inOrder.size() != 0))
        {
        	// 把preorder的第一個元素作為root
            root = new TreeNode(preOrder.get(0));
 
            //  Based upon the current node data seperate the traversals into leftPreorder, rightPreorder,
            //  leftInorder, rightInorder lists
            // 因為知道root節點了,所以根據root節點位置,把preorder,inorder分別劃分為 root左側 和 右側 的兩個子區間
            inorderPos = inOrder.indexOf(preOrder.get(0));		// inorder序列的分割點
            leftInorder = inOrder.subList(0, inorderPos);
            rightInorder = inOrder.subList(inorderPos + 1, inOrder.size());
 
            preorderPos = leftInorder.size();							// preorder序列的分割點
            leftPreOrder = preOrder.subList(1, preorderPos + 1);
            rightPreOrder = preOrder.subList(preorderPos + 1, preOrder.size());
 
            root.left = rebuildBinaryTreeRec(leftPreOrder, leftInorder);		// root的左子樹就是preorder和inorder的左側區間而形成的樹
            root.right = rebuildBinaryTreeRec(rightPreOrder, rightInorder);	// root的右子樹就是preorder和inorder的右側區間而形成的樹
        }
 
        return root;
	}
	
	/**
	 	14.  判斷二叉樹是不是完全二叉樹(迭代)
	  	若設二叉樹的深度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,
	  	第 h 層所有的結點都連續集中在最左邊,這就是完全二叉樹。
		有如下演算法,按層次(從上到下,從左到右)遍歷二叉樹,當遇到一個節點的左子樹為空時,
		則該節點右子樹必須為空,且後面遍歷的節點左右子樹都必須為空,否則不是完全二叉樹。
	 */
	public static boolean isCompleteBinaryTree(TreeNode root){
		if(root == null){
			return false;
		}
		
		Queue<TreeNode> queue = new LinkedList<TreeNode>();
		queue.add(root);
		boolean mustHaveNoChild = false;
		boolean result = true;
		
		while( !queue.isEmpty() ){
			TreeNode cur = queue.remove();
			if(mustHaveNoChild){	// 已經出現了有空子樹的節點了,後面出現的必須為葉節點(左右子樹都為空)  
				if(cur.left!=null || cur.right!=null){
					result = false;
					break;
				}
			} else {
				if(cur.left!=null && cur.right!=null){		// 如果左子樹和右子樹都非空,則繼續遍歷
					queue.add(cur.left);
					queue.add(cur.right);
				}else if(cur.left!=null && cur.right==null){	// 如果左子樹非空但右子樹為空,說明已經出現空節點,之後必須都為空子樹
					mustHaveNoChild = true;
					queue.add(cur.left);
				}else if(cur.left==null && cur.right!=null){	// 如果左子樹為空但右子樹非空,說明這棵樹已經不是完全二叉完全樹!
					result = false;
					break;
				}else{			// 如果左右子樹都為空,則後面的必須也都為空子樹
					mustHaveNoChild = true;
				}
			}
		}
		return result;
	}
	
	/**
	 * 14.  判斷二叉樹是不是完全二叉樹(遞迴)
	 * http://stackoverflow.com/questions/1442674/how-to-determine-whether-a-binary-tree-is-complete
	 * 
	 */
	public static boolean isCompleteBinaryTreeRec(TreeNode root){
//		Pair notComplete = new Pair(-1, false);
//		return !isCompleteBinaryTreeSubRec(root).equalsTo(notComplete);
		return isCompleteBinaryTreeSubRec(root).height != -1;
	}
	
	// 遞迴判斷是否滿樹(完美)
	public static boolean isPerfectBinaryTreeRec(TreeNode root){
		return isCompleteBinaryTreeSubRec(root).isFull;
	}
	
	// 遞迴,要建立一個Pair class來儲存樹的高度和是否已滿的資訊
	public static Pair isCompleteBinaryTreeSubRec(TreeNode root){
		if(root == null){
			return new Pair(0, true);
		}
		
		Pair left = isCompleteBinaryTreeSubRec(root.left);
		Pair right = isCompleteBinaryTreeSubRec(root.right);
		
		// 左樹滿節點,而且左右樹相同高度,則是唯一可能形成滿樹(若右樹也是滿節點)的情況
		if(left.isFull && left.height==right.height){
			return new Pair(1+left.height, right.isFull);
		}
		
		// 左樹非滿,但右樹是滿節點,且左樹高度比右樹高一
		// 注意到如果其左樹為非完全樹,則它的高度已經被設定成-1,
		// 因此不可能滿足第二個條件!
		if(right.isFull && left.height==right.height+1){
			return new Pair(1+left.height, false);
		}
		
		// 其他情況都是非完全樹,直接設定高度為-1
		return new Pair(-1, false);
	}
	
	private static class Pair{
		int height;				// 樹的高度
		boolean isFull;		// 是否是個滿樹

		public Pair(int height, boolean isFull) {
			this.height = height;
			this.isFull = isFull;
		}

		public boolean equalsTo(Pair obj){
			return this.height==obj.height && this.isFull==obj.isFull;
		}
	}
	
}