題目總結與代碼歸檔:
【劍指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
歡迎大家來一起交流學習