思路:
- 根據前序遍歷序列找到根節點的值
- 在中序遍歷序列中找到根節點的位置
- 找到左子樹根節點(中序遍歷中根結點的前一個位置,如果存在的話,就是左子樹的根結點)
- 找到右子樹根節點(中序遍歷中根結點的後一個位置,如果存在的話,就是右子樹的根結點)
- 遞歸地重建子樹(使用左子樹的前序、中序序列構建左子樹;使用右子樹的前序、中序序列構建右子樹)
例如下面這張圖(出自《劍指offer》):
下面是代碼:
/* 由前序遍歷序列和中序遍歷序列重建二叉樹 */
#include <iostream>
#include <cstdlib>
using namespace std;
struct Node
{
int val;
Node* left;
Node* right;
};
Node* construct(int* preOrder, int* inOrder, int length)
{
Node* root = (Node*)malloc(sizeof(Node)); // 爲子樹的根節點分配空間(我們把整棵樹的根節點也暫且當作子樹)
int rootVal = preOrder[0]; // 子樹的根結的值一定保存在前序遍歷序列的第一個位置
root->val = rootVal; // 設置子樹根結點的值
root->left = root->right = nullptr; // 先將左右子樹均設置爲空指針,避免出現野指針
// 如果前序遍歷和中序遍歷序列的長度爲1,說明只有一個結點,直接作爲葉結點返回即可
if (1 == length) return root;
// rootIn用於保存當前子樹的根節點在中序遍歷序列中的位置
// rootIn-1(如果存在的話)就是當前子樹的左子樹的根節點
// rootIn+1(如果存在的話)就是當前子樹的右子樹的根節點
int* rootIn = inOrder;
while (rootIn <= inOrder && *rootIn != rootVal) // 查找當前子樹的根節點在中序遍歷序列中的位置
++rootIn;
int lenL = rootIn - inOrder; // 左子樹的長度
int lenR = length - lenL - 1; // 右子樹的長度
if (lenL > 0) // 如果當前子樹的左子樹存在,遞歸的構建左子樹
root->left = construct(preOrder + 1, inOrder, lenL);
if (lenR > 0) // 如果當前子樹的右子樹存在,遞歸的構建左子樹
root->right = construct(preOrder + lenL + 1, rootIn + 1, lenR);
return root; // 返回當前子樹的根節點
}
void tranverse(Node* root) // 前序遍歷重建得到的二叉樹
{
if (nullptr == root) return;
cout << root->val << ' ';
tranverse(root->left);
tranverse(root->right);
}
int main()
{
int preOrderSeq[] = {1, 2, 4, 7, 3, 5, 6, 8};
int inOrderSeq[] = {4, 7, 2, 1, 5, 3, 8, 6};
const int length = 8;
Node* root = construct(preOrderSeq, inOrderSeq, length);
tranverse(root);
return 0;
}