劍指offer 面試題7:重建二叉樹【C++版本】

題目總結與代碼歸檔:
【劍指offer-2】題目目錄【C++版本】

GitHub代碼路徑: GitHub

面試題7

重建二叉樹

題目:輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2, 4, 7, 3, 5, 6, 8}和中序遍歷序列{4, 7, 2, 1, 5, 3, 8, 6},則重建出
樹節點定義如下:

struct BinaryTreeNode 
{
    int                    m_nValue; 
    BinaryTreeNode*        m_pLeft;  
    BinaryTreeNode*        m_pRight; 
};

解題思路

在這裏插入圖片描述
1、前序遍歷的第一個數字總是樹的根節點的值。中序遍歷中,根節點的值在序列的中間,其中左子樹的節點的值位於根節點的值的左邊,右子樹的節點的值位於根節點的值的右邊。如圖: 1 爲root節點 {2,4,7} 爲左子樹,{3,5,6,8}爲右子樹
2、將左子樹和右子樹看成新的樹,新的樹也滿足1。
3、將{2,4,7}看成新的數,那麼新樹的前序遍歷序列爲 {2,4,7} 中序遍歷序列爲{4,7,2},進一步按 1 2 操作。使用分治思想,採用遞歸方式處理。

代碼實現

分治
主邏輯代碼段,全代碼見GItHub

/*
preorder:	前序遍歷序列
inorder:	中序遍歷序列
length:		元素個數
*/
BinaryTreeNode* Construct(int* preorder, int* inorder, int length)
{
	if (preorder == nullptr || inorder == nullptr || length <= 0)
		return nullptr;

	return ConstructCore(preorder, preorder + length - 1,
		inorder, inorder + length - 1);
}

/*
遞歸主函數
startPreorder:	前序遍歷序列起始位置
endPreorder:	前序遍歷序列結束位置
startInorder:	中序遍歷序列起始位置
endInorder:		中序遍歷序列結束位置
eg: 以前序遍歷序列{1,2, 4, 7, 3, 5, 6, 8}和中序遍歷序列{4, 7, 2, 1, 5, 3, 8, 6}爲例子

*/
BinaryTreeNode* ConstructCore(int* startPreorder, int* endPreorder, int* startInorder, int* endInorder)
{
	// eg: startPreorder = 1 endPreorder = 8  startInorder = 4 endInorder = 6
	// 前序遍歷序列的第一個數字是根結點的值
	int rootValue = startPreorder[0];	// eg: 1
	// 創建根結點
	BinaryTreeNode* root = new BinaryTreeNode();	
	root->m_nValue = rootValue;
	root->m_pLeft = root->m_pRight = nullptr;

	// 遞歸終止條件,和異常處理
	if (startPreorder == endPreorder) // 前序遍歷序列起始位置 == 前序遍歷序列結束位置
	{
		// 中序遍歷序列起始位置 == 中序遍歷序列結束位置 並且 此時 前序中序都應該指向同一個結點其值相等 *startPreorder == *startInorder
		if(startInorder == endInorder && *startPreorder == *startInorder)
			return root;
		else // 否則拋出異常
			throw std::exception("Invalid input.");
	}

	// 遍歷 中序遍歷序列 通過 根結點的值 找到 其位置,從而知道 其左右子樹數據
	int* rootInorder = startInorder;
	while (rootInorder <= endInorder&& * rootInorder != rootValue)
		rootInorder++;
	// 異常處理,可能數據有誤導
	if (rootInorder == endInorder && * rootInorder != rootValue)
	{
		throw std::exception("Invalid input.");
	}

	// 這時 rootInorder 指向 根結點數, 那麼 rootInorder - 1 爲 左子樹 中序遍歷序列結束位置 eg: 2  startInorder 爲 左子樹 中序遍歷序列起始位置 eg: 4
	int leftLength = rootInorder - startInorder; // 左子樹數據個數 eg: leftLength = 3
	// 計數 左子樹 前序遍歷序列結束位置   startPreorder + 1  爲 左子樹 前序遍歷序列起始位置 eg: 2   leftPreorderEnd 爲 左子樹 前序遍歷序列結束位置 eg: 7
	int* leftPreorderEnd = startPreorder + leftLength;	// 
	// 分爲 左右子樹處理
	if (leftLength > 0) // 存在 左子樹
	{
		// 構建左子樹 遞歸  
		// startPreorder + 1 : 左子樹 前序遍歷序列起始位置;  leftPreorderEnd : 左子樹 前序遍歷序列結束位置
		// startInorder: 左子樹 中序遍歷序列起始位置;   rootInorder - 1  :左子樹 中序遍歷序列結束位置
		root->m_pLeft = ConstructCore(startPreorder + 1, leftPreorderEnd,
			startInorder, rootInorder - 1);
	}
	// 計數 右子樹數據個數 。 (endPreorder - startPreorder) 原樹 數據個數, 也就是 左子樹數據個數 + 右子樹數據個數
	int rightLength = (endPreorder - startPreorder) - leftLength;
	if (rightLength > 0)	// 存在 右子樹
	{	
		// 構建右子樹 遞歸  
		// leftPreorderEnd + 1 : 右子樹 前序遍歷序列起始位置;  endPreorder : 右子樹 前序遍歷序列結束位置
		// rootInorder + 1: 右子樹 中序遍歷序列起始位置;   endInorder  :右子樹 中序遍歷序列結束位置
		root->m_pRight = ConstructCore(leftPreorderEnd + 1, endPreorder,
			rootInorder + 1, endInorder);
	}

	return root;
}

參考資料:


GitHub鏈接:
https://github.com/lichangke
CSDN首頁:
https://me.csdn.net/leacock1991
歡迎大家來一起交流學習

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