Série: Algorithms
cpp
415 lignes
· Mis à jour 2026-04-05
tree_construct_from_traversals.cpp
Algorithms/tree_construct_from_traversals.cpp
/*
* ============================================================================
* 由遍歷結果建構二元樹 — Preorder+Inorder / Inorder+Postorder
* File: tree_construct_from_traversals.cpp
*
* 核心原理:
* - Preorder 第一個元素 = root;Postorder 最後一個元素 = root
* - 在 Inorder 中找到 root → 左邊 = 左子樹, 右邊 = 右子樹
* - 用 HashMap 預處理 Inorder,使查找 root 位置 O(1) → 整體 O(N)
*
* 涵蓋:
* 1. Preorder + Inorder → 建構唯一二元樹
* 2. Inorder + Postorder → 建構唯一二元樹
* 3. 驗證: 建構後再做遍歷,確認和輸入一致
* 4. 視覺化輸出
* 5. 說明為什麼 Preorder + Postorder 不能唯一確定
*
* Compile: g++ -std=c++11 -Wall -o tree_construct_from_traversals tree_construct_from_traversals.cpp
* ============================================================================
*/
#include <iostream>
#include <vector>
#include <unordered_map>
#include <string>
#include <queue>
using namespace std;
/* ============================================================================
* 節點結構
* ============================================================================ */
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int v) : val(v), left(nullptr), right(nullptr) {}
};
/* ============================================================================
* 1. Preorder + Inorder → 建構二元樹
*
* 原理:
* Preorder: [Root, <左子樹的 Preorder>, <右子樹的 Preorder>]
* Inorder: [<左子樹的 Inorder>, Root, <右子樹的 Inorder>]
*
* 步驟:
* 1. pre[preL] = root
* 2. 在 inorder 中找到 root 的位置 rootIdx (用 HashMap → O(1))
* 3. leftSize = rootIdx - inL
* 4. 遞迴建構左子樹: pre[preL+1 .. preL+leftSize], in[inL .. rootIdx-1]
* 5. 遞迴建構右子樹: pre[preL+leftSize+1 .. preR], in[rootIdx+1 .. inR]
*
* 時間: O(N) (HashMap 查找 O(1))
* 空間: O(N) (HashMap + 遞迴 stack)
* ============================================================================ */
TreeNode* buildFromPreIn(const vector<int>& preorder, int preL, int preR,
const vector<int>& inorder, int inL, int inR,
unordered_map<int, int>& inMap) {
// Base case: 區間為空
if (preL > preR) return nullptr;
// Step 1: Preorder 的第一個元素是當前子樹的 root
int rootVal = preorder[preL];
TreeNode* root = new TreeNode(rootVal);
// Step 2: 用 HashMap 找 root 在 Inorder 中的位置 — O(1)
int rootIdx = inMap[rootVal];
// Step 3: 左子樹的節點數
int leftSize = rootIdx - inL;
// Step 4: 遞迴建構左子樹
// Preorder 中左子樹佔: [preL+1, preL+leftSize]
// Inorder 中左子樹佔: [inL, rootIdx-1]
root->left = buildFromPreIn(preorder, preL + 1, preL + leftSize,
inorder, inL, rootIdx - 1,
inMap);
// Step 5: 遞迴建構右子樹
// Preorder 中右子樹佔: [preL+leftSize+1, preR]
// Inorder 中右子樹佔: [rootIdx+1, inR]
root->right = buildFromPreIn(preorder, preL + leftSize + 1, preR,
inorder, rootIdx + 1, inR,
inMap);
return root;
}
// 驅動函式
TreeNode* buildFromPreorderInorder(const vector<int>& preorder,
const vector<int>& inorder) {
int n = (int)preorder.size();
if (n == 0) return nullptr;
// 預處理: 建立 Inorder 值 → 索引的 HashMap
unordered_map<int, int> inMap;
for (int i = 0; i < n; ++i) {
inMap[inorder[i]] = i;
}
return buildFromPreIn(preorder, 0, n - 1, inorder, 0, n - 1, inMap);
}
/* ============================================================================
* 2. Inorder + Postorder → 建構二元樹
*
* 原理:
* Postorder: [<左子樹的 Postorder>, <右子樹的 Postorder>, Root]
* Inorder: [<左子樹的 Inorder>, Root, <右子樹的 Inorder>]
*
* 步驟:
* 1. post[postR] = root (Postorder 的最後一個元素)
* 2. 在 inorder 中找到 root 的位置 rootIdx
* 3. rightSize = inR - rootIdx
* 4. 遞迴建構右子樹: post[postR-rightSize .. postR-1], in[rootIdx+1 .. inR]
* 5. 遞迴建構左子樹: post[postL .. postR-rightSize-1], in[inL .. rootIdx-1]
*
* 時間: O(N)
* ============================================================================ */
TreeNode* buildFromInPost(const vector<int>& inorder, int inL, int inR,
const vector<int>& postorder, int postL, int postR,
unordered_map<int, int>& inMap) {
// Base case
if (postL > postR) return nullptr;
// Step 1: Postorder 的最後一個元素是 root
int rootVal = postorder[postR];
TreeNode* root = new TreeNode(rootVal);
// Step 2: 找 root 在 Inorder 中的位置
int rootIdx = inMap[rootVal];
// Step 3: 右子樹的節點數
int rightSize = inR - rootIdx;
// Step 4: 遞迴建構右子樹
// Postorder 中右子樹佔: [postR-rightSize, postR-1]
// Inorder 中右子樹佔: [rootIdx+1, inR]
root->right = buildFromInPost(inorder, rootIdx + 1, inR,
postorder, postR - rightSize, postR - 1,
inMap);
// Step 5: 遞迴建構左子樹
// Postorder 中左子樹佔: [postL, postR-rightSize-1]
// Inorder 中左子樹佔: [inL, rootIdx-1]
root->left = buildFromInPost(inorder, inL, rootIdx - 1,
postorder, postL, postR - rightSize - 1,
inMap);
return root;
}
// 驅動函式
TreeNode* buildFromInorderPostorder(const vector<int>& inorder,
const vector<int>& postorder) {
int n = (int)inorder.size();
if (n == 0) return nullptr;
unordered_map<int, int> inMap;
for (int i = 0; i < n; ++i) {
inMap[inorder[i]] = i;
}
return buildFromInPost(inorder, 0, n - 1, postorder, 0, n - 1, inMap);
}
/* ============================================================================
* 遍歷函式(用來驗證建構結果)
* ============================================================================ */
void getPreorder(TreeNode* node, vector<int>& result) {
if (!node) return;
result.push_back(node->val);
getPreorder(node->left, result);
getPreorder(node->right, result);
}
void getInorder(TreeNode* node, vector<int>& result) {
if (!node) return;
getInorder(node->left, result);
result.push_back(node->val);
getInorder(node->right, result);
}
void getPostorder(TreeNode* node, vector<int>& result) {
if (!node) return;
getPostorder(node->left, result);
getPostorder(node->right, result);
result.push_back(node->val);
}
void getLevelOrder(TreeNode* root, vector<int>& result) {
if (!root) return;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
TreeNode* cur = q.front();
q.pop();
result.push_back(cur->val);
if (cur->left) q.push(cur->left);
if (cur->right) q.push(cur->right);
}
}
/* ============================================================================
* 視覺化輸出
* ============================================================================ */
void printTree(TreeNode* node, const string& prefix = "", bool isLeft = true) {
if (!node) return;
if (node->right) {
printTree(node->right, prefix + (isLeft ? "│ " : " "), false);
}
cout << prefix;
cout << (isLeft ? "└── " : "┌── ");
cout << node->val << endl;
if (node->left) {
printTree(node->left, prefix + (isLeft ? " " : "│ "), true);
}
}
/* ============================================================================
* 輔助: 印出 vector
* ============================================================================ */
void printVec(const string& label, const vector<int>& v) {
cout << label;
for (size_t i = 0; i < v.size(); ++i) {
if (i > 0) cout << " ";
cout << v[i];
}
cout << endl;
}
/* ============================================================================
* 釋放記憶體
* ============================================================================ */
void deleteTree(TreeNode* node) {
if (!node) return;
deleteTree(node->left);
deleteTree(node->right);
delete node;
}
/* ============================================================================
* 主程式
* ============================================================================ */
int main() {
cout << "============================================================" << endl;
cout << " Construct Binary Tree from Traversals" << endl;
cout << "============================================================\n" << endl;
/*
* 目標樹:
* 1
* / \
* 2 3
* / \ / \
* 4 5 6 7
*/
// ─────────────────────────────────────────────
// 測試 1: Preorder + Inorder
// ─────────────────────────────────────────────
cout << "【1. Preorder + Inorder → Build Tree】\n" << endl;
vector<int> preorder = {1, 2, 4, 5, 3, 6, 7};
vector<int> inorder = {4, 2, 5, 1, 6, 3, 7};
printVec(" Input Preorder: ", preorder);
printVec(" Input Inorder: ", inorder);
cout << endl;
TreeNode* tree1 = buildFromPreorderInorder(preorder, inorder);
cout << " Built tree:" << endl;
printTree(tree1);
cout << endl;
// 驗證: 重新做遍歷看是否一致
vector<int> verify_pre, verify_in, verify_post, verify_level;
getPreorder(tree1, verify_pre);
getInorder(tree1, verify_in);
getPostorder(tree1, verify_post);
getLevelOrder(tree1, verify_level);
printVec(" Verify Preorder: ", verify_pre);
printVec(" Verify Inorder: ", verify_in);
printVec(" Verify Postorder: ", verify_post);
printVec(" Verify Level-order: ", verify_level);
cout << " Preorder match: " << (verify_pre == preorder ? "YES" : "NO") << endl;
cout << " Inorder match: " << (verify_in == inorder ? "YES" : "NO") << endl;
cout << endl;
// ─────────────────────────────────────────────
// 測試 2: Inorder + Postorder
// ─────────────────────────────────────────────
cout << "【2. Inorder + Postorder → Build Tree】\n" << endl;
vector<int> postorder = {4, 5, 2, 6, 7, 3, 1};
printVec(" Input Inorder: ", inorder);
printVec(" Input Postorder: ", postorder);
cout << endl;
TreeNode* tree2 = buildFromInorderPostorder(inorder, postorder);
cout << " Built tree:" << endl;
printTree(tree2);
cout << endl;
// 驗證
vector<int> v2_in, v2_post;
getInorder(tree2, v2_in);
getPostorder(tree2, v2_post);
printVec(" Verify Inorder: ", v2_in);
printVec(" Verify Postorder: ", v2_post);
cout << " Inorder match: " << (v2_in == inorder ? "YES" : "NO") << endl;
cout << " Postorder match: " << (v2_post == postorder ? "YES" : "NO") << endl;
cout << endl;
// ─────────────────────────────────────────────
// 測試 3: 較大的樹
// ─────────────────────────────────────────────
cout << "【3. Larger Tree Test】\n" << endl;
/*
* 50
* / \
* 30 70
* / \ / \
* 20 40 60 80
* / \
* 10 45
*/
vector<int> pre3 = {50, 30, 20, 10, 40, 45, 70, 60, 80};
vector<int> in3 = {10, 20, 30, 40, 45, 50, 60, 70, 80};
printVec(" Preorder: ", pre3);
printVec(" Inorder: ", in3);
TreeNode* tree3 = buildFromPreorderInorder(pre3, in3);
cout << "\n Built tree:" << endl;
printTree(tree3);
vector<int> v3_post;
getPostorder(tree3, v3_post);
printVec("\n Derived Postorder: ", v3_post);
// 用 Inorder + Postorder 重建,驗證一致
TreeNode* tree3b = buildFromInorderPostorder(in3, v3_post);
vector<int> v3b_pre;
getPreorder(tree3b, v3b_pre);
cout << " Rebuild from In+Post → Preorder match: "
<< (v3b_pre == pre3 ? "YES" : "NO") << endl;
cout << endl;
// ─────────────────────────────────────────────
// 測試 4: 只有左子樹(歪斜樹)
// ─────────────────────────────────────────────
cout << "【4. Skewed Tree (left-only)】\n" << endl;
vector<int> pre4 = {1, 2, 3, 4};
vector<int> in4 = {4, 3, 2, 1};
TreeNode* tree4 = buildFromPreorderInorder(pre4, in4);
cout << " Built tree:" << endl;
printTree(tree4);
cout << endl;
// ─────────────────────────────────────────────
// 測試 5: 單節點
// ─────────────────────────────────────────────
cout << "【5. Single Node】\n" << endl;
vector<int> pre5 = {42};
vector<int> in5 = {42};
TreeNode* tree5 = buildFromPreorderInorder(pre5, in5);
cout << " Built tree:" << endl;
printTree(tree5);
cout << endl;
// ─────────────────────────────────────────────
// 說明 6: 為什麼 Preorder + Postorder 不唯一
// ─────────────────────────────────────────────
cout << "【6. Why Preorder + Postorder is NOT unique】\n" << endl;
cout << " Preorder: [1, 2]" << endl;
cout << " Postorder: [2, 1]" << endl;
cout << endl;
cout << " Possible tree A: Possible tree B:" << endl;
cout << " 1 1" << endl;
cout << " / \\" << endl;
cout << " 2 2" << endl;
cout << endl;
cout << " Both have the same Preorder and Postorder!" << endl;
cout << " Without Inorder, we cannot distinguish left from right." << endl;
// 清理
deleteTree(tree1);
deleteTree(tree2);
deleteTree(tree3);
deleteTree(tree3b);
deleteTree(tree4);
deleteTree(tree5);
cout << "\n============================================================" << endl;
cout << " Done." << endl;
cout << "============================================================" << endl;
return 0;
}
Articles liés
Algorithms
java
Mis à jour 2026-03-02
#include <iostream>.java
#include <iostream>.java — java source code from the Algorithms learning materials (Algorithms/#include <iostream>.java).
Lire l'article →
Algorithms
cpp
Mis à jour 2026-04-07
748.cpp
748.cpp — cpp source code from the Algorithms learning materials (Algorithms/748.cpp).
Lire l'article →
Algorithms
cpp
Mis à jour 2026-04-07
827.cpp
827.cpp — cpp source code from the Algorithms learning materials (Algorithms/827.cpp).
Lire l'article →
Algorithms
cpp
Mis à jour 2026-04-07
827_best_greedy.cpp
827_best_greedy.cpp — cpp source code from the Algorithms learning materials (Algorithms/827_best_greedy.cpp).
Lire l'article →
Algorithms
cpp
Mis à jour 2026-04-07
8402.cpp
8402.cpp — cpp source code from the Algorithms learning materials (Algorithms/8402.cpp).
Lire l'article →
Algorithms
cpp
Mis à jour 2026-04-07
860.cpp
860.cpp — cpp source code from the Algorithms learning materials (Algorithms/860.cpp).
Lire l'article →