1. 程式人生 > >劍指Offer(第二版)面試題7:重建二叉樹

劍指Offer(第二版)面試題7:重建二叉樹

劍指Offer面試題7:重建二叉樹

題目:輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建該二叉樹。
假設輸入的前序遍歷和中序遍歷中都不包含重複的數字。例如前序遍歷序列為{1,2,4,5,3,7,6}和中序遍歷序列{4,2,5,1,7,3,6},則可以重建一棵二叉樹。如圖所示:

   1  
 /  \  
2     3  
       /  \    /  \  
     4   5  7  6  

思路:通過前序遍歷序列可以得到根節點,中序遍歷序列可以得到左右分別有哪些節點;通過,在左右子樹中使用此方法進行遞迴,最後,可以重建二叉樹。

已知二叉樹的前序遍歷和中序遍歷:
PreOrder:           GDAFEMHZ
InOrder:            ADEFGHMZ

我們基於一個事實:中序遍歷一定是 { 左子樹中的節點集合 },root,{ 右子樹中的節點集合 },前序遍歷的作用就是找到每棵子樹的root位置。

演算法
輸入:前序遍歷,中序遍歷
1、尋找樹的root,前序遍歷的第一節點G就是root。
2、觀察前序遍歷GDAFEMHZ,知道了G是root,剩下的節點必然在root的左或右子樹中的節點。
3、觀察中序遍歷ADEFGHMZ。其中root節點G左側的ADEF必然是root的左子樹中的節點,G右側的HMZ必然是root的右子樹中的節點,root不在中序遍歷的末尾或開始就說明根節點的兩顆子樹都不為空。
4、觀察左子樹ADEF,按照前序遍歷的順序來排序為DAFE,因此左子樹的根節點為D,並且A是左子樹的左子樹中的節點,EF是左子樹的右子樹中的節點。
5、同樣的道理,觀察右子樹節點HMZ,前序為MHZ,因此右子樹的根節點為M,左子節點H,右子節點Z。

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/*
 * 劍指Offer面試題7:重建二叉樹
 * 題目:輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建該二叉樹。
 * 假設輸入的前序遍歷和中序遍歷中都不包含重複的數字。例如前序遍歷序列為{1,2,4,5,3,7,6}
 * 和中序遍歷序列{4,2,5,1,7,3,6},則可以重建一棵二叉樹。如圖所示:
 *  
		    1  
		  /  \  
		 2    3  
		/ \  / \  
	   4  5 7   6  
	  
 */
public class Test {

	public static void main(String[] args) {
		TreeNode root = 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);
        TreeNode r7 = new TreeNode(7);
          
        root.left = r2;  
        root.right = r3;  
        r2.left = r4;  
        r2.right = r5;  
        r3.right = r6; 
        r3.left = r7;
        List<Integer> preList = new ArrayList<>();
        preList.add(1);
        preList.add(2);
        preList.add(4);
        preList.add(5);
        preList.add(3);
        preList.add(7);
        preList.add(6);
        
        List<Integer> inList = new ArrayList<>();
        inList.add(4);
        inList.add(2);
        inList.add(5);
        inList.add(1);
        inList.add(7);
        inList.add(3);
        inList.add(6);
        
        
        TreeNode reBuildTree = reBuildTree(preList, inList);
        levelTraversal(reBuildTree);
        
	}
	/**
	 * 思路:使用遞迴的思路解決問題,分別在左右子樹中進行操作
	 * @param preOrderList
	 * @param inOrderList
	 * @return
	 */
	private static TreeNode reBuildTree(List<Integer> preOrderList,List<Integer> inOrderList){
		if(preOrderList==null||inOrderList==null)
			return null;
		TreeNode root = null; // 要返回的根結點
		List<Integer> leftPreOrderList;
		List<Integer> rightPreOrderList;
		List<Integer> leftInOrderList;
		List<Integer> rightInOrderList;
		int inOrderPos;
		int preOrderPos;
		
		if(preOrderList.size()!=0&&inOrderList.size()!=0){
			// 把preorder的第一個元素作為root  
			root = new TreeNode(preOrderList.get(0));
			// 因為知道root節點了,所以根據root節點位置,把preorder,inorder分別
            //劃分為 root左側 和 右側 的兩個子區間  
			inOrderPos = inOrderList.indexOf(preOrderList.get(0));   // inorder序列的分割點 
			leftInOrderList = inOrderList.subList(0, inOrderPos);
			rightInOrderList = inOrderList.subList(inOrderPos+1, inOrderList.size());
			
			preOrderPos = leftInOrderList.size();  // preorder序列的分割點
			leftPreOrderList = preOrderList.subList(1, preOrderPos+1);
			rightPreOrderList = preOrderList.subList(preOrderPos+1, preOrderList.size());
			// root的左子樹就是preorder和inorder的左側區間而形成的樹  
			root.left = reBuildTree(leftPreOrderList, leftInOrderList);
			// root的右子樹就是preorder和inorder的右側區間而形成的樹  
			root.right = reBuildTree(rightPreOrderList, rightInOrderList);
			
		}
		return root;
		
	}
	/**
	 * 分層遍歷二叉樹,使用一個佇列,也就是寬度優先遍歷
	 * @param root
	 */
	public static void levelTraversal(TreeNode root){
    	if(root==null)
    		return ;
    	
    	LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
    	queue.add(root);
    	while(!queue.isEmpty()){
    		TreeNode cur = queue.remove();
    		System.out.print(cur.val+" ");
    		if(cur.left!=null)
    			queue.add(cur.left);
    		if(cur.right!=null)
    			queue.add(cur.right);
    		
    	}
    }
	

	
}
//建立二叉樹節點類
class TreeNode{
	int val;
	TreeNode left;
	TreeNode right;
	public TreeNode(int val){
		this.val = val;
	}
	
}

引數為陣列時:

public TreeNode reConstructBinaryTree(int[] pre,int[] in) {
        if(pre==null||in==null)
        	return null;
        TreeNode root = null;
       
        int[] preLeft = null;
        int[] preRight = null;
        
        int[] inLeft = null;
        int[] inRight = null;
        
        if(pre.length!=0&&in.length!=0){
        	// 得到root節點
        	root = new TreeNode(pre[0]);
        	for (int i = 0; i < in.length; i++) {
				if(pre[0]==in[i]){
					inLeft = Arrays.copyOfRange(in, 0, i);
					inRight = Arrays.copyOfRange(in, i+1, in.length);
					preLeft = Arrays.copyOfRange(pre, 1, i+1);
					preRight = Arrays.copyOfRange(pre, i+1, pre.length);
				}
			}
        	root.left = reConstructBinaryTree(preLeft, inLeft);
        	root.right = reConstructBinaryTree(preRight, inRight);
        	
        }
        return root;
    }

此時需要用到Arrays.copyRange(int[ ]  original , int from, int end)

如果對你有幫助,記得點贊哦~歡迎大家關注我的部落格,可以進群366533258一起交流學習哦~