題目:這道題目是一道面試題,先序遍曆和中序遍曆以數組的形式給出,要求我們根據這兩個條件重構出二叉樹。
是一棵二叉樹
// 6// / \// 5 7 // / \ \// 2 4 8
先序遍曆:6,5,2,4,7,8
中序遍曆:2,5,4,6,7,8
思路:二叉樹先序遍曆的定義:1,先輸出根結點2,再輸出左子樹3,再輸出右子樹,因此先序遍曆時,根結點總會出現在數組開頭,中序遍曆時,根結點可以將二叉樹分為左右子樹。
因此,重構二叉樹的步驟可以用以頂向下的方法:
1,先找到根結點,
2,在中序遍曆中找到根結點位置,(可以將二叉樹分為左子樹和右子樹)
3.用同樣的辦法構造左子樹
4.用同樣的辦法構造右子樹。
例如:
1.找到根結點6,因此左子樹是2,5,4和右子樹是7,8
2.找到左邊根結點5,可將子二叉樹分為2和4,因此左邊確定。
3,找到右邊根結點7,因此可確定右子樹8。
根據四步走可以寫出如出代碼:
node* Build_Tree(int* prec,int* inorder,int len){//步驟1:建立根結點node* root=new node(prec[0]);//步驟2:在中序遍曆中找到根結點索引,分割左右子樹int SubTreeLen=0;while(SubTreeLen < len && inorder[SubTreeLen] != prec[0])++SubTreeLen;if(SubTreeLen > 0){//步驟2:重建左子樹,並且將根結點root的left指向左子樹root->left=Build_Tree(prec+1,inorder,SubTreeLen);}if(len-SubTreeLen-1 > 0){//步驟2:重建右子樹,並且將根結點root的left指向右子樹root->right=Build_Tree(prec+1+SubTreeLen,inorder+1+SubTreeLen,len-SubTreeLen-1);}return root;}
接下來的步驟就是進行程式測試,利用邊界值等去測試程式的健壯性。
可以利用下面的二叉樹去測試:
// 普通二叉樹// 1// / \// 2 3 // / / \// 4 5 6// \ /// 7 8int preorder[length] = {1, 2, 4, 7, 3, 5, 6, 8};int inorder[length] = {4, 7, 2, 1, 5, 3, 8, 6};// 所有結點都沒有右子結點// 1// / // 2 // / // 3 // /// 4// /// 5int preorder[length] = {1, 2, 3, 4, 5};int inorder[length] = {5, 4, 3, 2, 1};// 所有結點都沒有左子結點// 1// \ // 2 // \ // 3 // \// 4// \// 5int preorder[length] = {1, 2, 3, 4, 5};int inorder[length] = {1, 2, 3, 4, 5};// 樹中只有一個結點 int preorder[length] = {1}; int inorder[length] = {1};// 完全二叉樹// 1// / \// 2 3 // / \ / \// 4 5 6 7 int preorder[length] = {1, 2, 4, 5, 3, 6, 7}; int inorder[length] = {4, 2, 5, 1, 6, 3, 7};// 輸入null 指標// 輸入的兩個序列不匹配 int preorder[length] = {1, 2, 4, 5, 3, 6, 7}; int inorder[length] = {4, 2, 8, 1, 6, 3, 7};
上述測試例子引自於:《劍指Offer——名企面試官精講典型編程題》
經過測試,當輸入null 指標和兩個序列不匹配時,原始的程式無辦法處理,可以針對性處理如下:
#include<iostream>#include <exception>using std::cout;using std::endl;struct node{int value;node* left;node* right;node(int v):value(v),left(NULL),right(NULL){}};node* Build_Tree(int* prec,int* inorder,int len){if(!prec || !inorder || len <=0 ){cout<<"Empty Input!"<<endl;exit(1);//暴力關機}//步驟1:建立根結點node* root=new node(prec[0]);//步驟2:在中序遍曆中找到根結點索引,分割左右子樹int SubTreeLen=0;while(SubTreeLen < len && inorder[SubTreeLen] != prec[0])++SubTreeLen;if(SubTreeLen == len)//越界了,說明兩個數組無法構造出二叉樹{cout<<"Wrong Input!"<<endl;exit(1);//暴力關機}if(SubTreeLen > 0){//步驟2:重建左子樹,並且將根結點root的left指向左子樹root->left=Build_Tree(prec+1,inorder,SubTreeLen);}if(len-SubTreeLen-1 > 0){//步驟2:重建右子樹,並且將根結點root的left指向右子樹root->right=Build_Tree(prec+1+SubTreeLen,inorder+1+SubTreeLen,len-SubTreeLen-1);}return root;}void prec_tree_walk(node* z){if(!z)return ;cout<<z->value<<' ';prec_tree_walk(z->left);prec_tree_walk(z->right);}void inorder_tree_walk(node* z){if(!z)return ;inorder_tree_walk(z->left);cout<<z->value<<' ';inorder_tree_walk(z->right);}int main(){int prec[] = {1, 2, 4, 7, 3, 5, 6, 8};int Inorder[] = {4, 7, 2, 1, 5, 3, 8, 6};int len=sizeof(prec)/sizeof(prec[0]);node* root=Build_Tree(prec,Inorder,len);prec_tree_walk(root);cout<<endl;inorder_tree_walk(root);return 0;}
中途發現的編譯錯誤記錄如下:點擊開啟連結