- 重建二叉树(P55)(面试题6)
- 前/中/后序遍历
重建二叉树(P55)(面试题6)
先捎带提一下二叉树的一些重要知识如下:
- (1)前序遍历:根->左->右 (2)中序遍历:左->根->右 (3)后序遍历:左->右->根
- 宽度优先遍历:一层一层遍历树的结点
- 二叉搜索树:左子节点≤根结点≤右子结点(树的特例,O(logn)时间根据值找到结点)
- 堆:最大、最小堆,用于快速查找
- 红黑树:把树中的结点定义为红、黑两种颜色,并通过规则确保从根结点到叶结点的最长路径的长度不超过最短路径的2倍
题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都【
不含重复的数字】。例如输入前序遍历{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出图2.6所示的二叉树并输出它的头结点。二叉树结点定义如下:
思路如下:
- 先判断传入前序、中序是否空,然后再进入递归函数
- 新建一个根结点,前序的第一个值为根,初始化其左右子树为NULL
- 设定终止条件为找到叶结点(即前序和中序都只剩一个值 & 相等)
- 找出中序里根的位置,数出左子树长度,由此可确定前序里左子树的位置
- 递归构建左右子树
- 返回根结点
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
struct BinaryTreeNode
{
int m_nValue;
BinaryTreeNode *m_pLeft;
BinaryTreeNode *m_pRight;
};
BinaryTreeNode* Construct(const vector<int> &preorder,const vector<int> &inorder); //重建函数入口
BinaryTreeNode* ConstructCore(const vector<int> &preorder,const vector<int> &inorder,
int nStartPre,int nEndPre,
int nStartIn ,int nEndIn); //重建函数核心
void Test(char* testName, int* preorder, int* inorder, int length); //测试函数主体
void PreorderTraverse(BinaryTreeNode *pRoot);//先序遍历
void InorderTraverse(BinaryTreeNode *pRoot); //中序遍历
void PostorderTraverse(BinaryTreeNode *pRoot); //后序遍历
BinaryTreeNode* ConstructCore(const vector<int> &preorder,const vector<int> &inorder,
int nStartPre,int nEndPre,
int nStartIn ,int nEndIn)
{
/*****1.每次递归产生一个结点,前序的第一个值是根*****/
BinaryTreeNode *pRoot = new BinaryTreeNode();
pRoot->m_nValue = preorder[nStartPre]; //前序的第一个值是根
pRoot->m_pLeft=pRoot->m_pRight=NULL;
/*****设定终止条件*****/
if (nStartPre == nEndPre)
{
if (nStartIn == nEndIn && preorder[nStartPre] == inorder[nStartIn])
return pRoot;
else
throw exception("无效输入,叶结点不同,无法构建二叉树");
}
/******2.根据pRoot的值,将中序分为 左子树|根|右子树,构建左右子树*******/
//在中序遍历中找到根结点的位置
int nRootIn = nStartIn;
while(nRootIn <= nEndIn && inorder[nRootIn] != pRoot->m_nValue)
++nRootIn;
if(nRootIn > nEndIn) //中序遍历找不到根结点
throw exception("无效输入,中序遍历中找不到根结点");
int nLeftSubTreeLength = nRootIn-nStartIn; //计算左子树长度,用于划分前序为 根|左子树|右子树
int nLeftSubTreeEnd = nStartPre+nLeftSubTreeLength; //前序遍历中左子树的右边界
//构建左右子树,要先判断是否有左右子树
if(nLeftSubTreeLength > 0) //构建左子树
pRoot->m_pLeft = ConstructCore(preorder,inorder,
nStartPre+1,nLeftSubTreeEnd,nStartIn,nRootIn-1);
if(nLeftSubTreeLength < nEndIn - nStartIn) //构建右子树
pRoot->m_pRight = ConstructCore(preorder,inorder,
nLeftSubTreeEnd+1,nEndPre,nRootIn+1,nEndIn);
return pRoot;
}
BinaryTreeNode* Construct(const vector<int> &preorder,const vector<int> &inorder)
{
if(preorder.empty() || inorder.empty())
return NULL;
return ConstructCore(preorder,inorder,0,
preorder.size()-1,0,inorder.size()-1);
}
void PreorderTraverse(BinaryTreeNode *pRoot) //先序遍历
{
if(pRoot == NULL)
return;
cout<<pRoot->m_nValue<<" ";
PreorderTraverse(pRoot->m_pLeft);
PreorderTraverse(pRoot->m_pRight);
}
void InorderTraverse(BinaryTreeNode *pRoot) //中序遍历
{
if(pRoot == NULL)
return;
InorderTraverse(pRoot->m_pLeft);
cout<<pRoot->m_nValue<<" ";
InorderTraverse(pRoot->m_pRight);
}
void PostorderTraverse(BinaryTreeNode *pRoot) //后序遍历
{
if(pRoot == NULL)
return;
PostorderTraverse(pRoot->m_pLeft);
PostorderTraverse(pRoot->m_pRight);
cout<<pRoot->m_nValue<<" ";
}
void Test(char* testName, int* preorder, int* inorder, int length) //测试函数主体
{
if(testName != NULL)
printf("%s begins:\n", testName);
printf("The preorder sequence is: ");
for(int i = 0; i < length; ++ i)
printf("%d ", preorder[i]);
printf("\n");
printf("The inorder sequence is: ");
for(int i = 0; i < length; ++ i)
printf("%d ", inorder[i]);
printf("\n");
vector<int> vnPreorder(preorder,preorder+length);
vector<int> vnInorder(inorder,inorder+length);
try
{
BinaryTreeNode* root = Construct(vnPreorder, vnInorder);
PreorderTraverse(root);cout<<" 先序遍历"<<endl;
InorderTraverse(root);cout<<" 中序遍历"<<endl;
PostorderTraverse(root);cout<<" 后序遍历"<<endl;
}
catch(exception& exception)
{
cout<<exception.what()<<endl;
}
}
// 普通二叉树
// 1
// / \
// 2 3
// / / \
// 4 5 6
// \ /
// 7 8
void Test1()
{
const int length = 8;
int preorder[length] = {1, 2, 4, 7, 3, 5, 6, 8};
int inorder[length] = {4, 7, 2, 1, 5, 3, 8, 6};
Test("Test1", preorder, inorder, length);
}
// 所有结点都没有右子结点
// 1
// /
// 2
// /
// 3
// /
// 4
// /
// 5
void Test2()
{
const int length = 5;
int preorder[length] = {1, 2, 3, 4, 5};
int inorder[length] = {5, 4, 3, 2, 1};
Test("Test2", preorder, inorder, length);
}
// 所有结点都没有左子结点
// 1
// \
// 2
// \
// 3
// \
// 4
// \
// 5
void Test3()
{
const int length = 5;
int preorder[length] = {1, 2, 3, 4, 5};
int inorder[length] = {1, 2, 3, 4, 5};
Test("Test3", preorder, inorder, length);
}
// 树中只有一个结点
void Test4()
{
const int length = 1;
int preorder[length] = {1};
int inorder[length] = {1};
Test("Test4", preorder, inorder, length);
}
// 完全二叉树
// 1
// / \
// 2 3
// / \ / \
// 4 5 6 7
void Test5()
{
const int length = 7;
int preorder[length] = {1, 2, 4, 5, 3, 6, 7};
int inorder[length] = {4, 2, 5, 1, 6, 3, 7};
Test("Test5", preorder, inorder, length);
}
// 输入空指针
void Test6()
{
Test("Test6", NULL, NULL, 0);
}
// 输入的两个序列不匹配
void Test7()
{
const int length = 7;
int preorder[length] = {1, 2, 4, 5, 3, 6, 7};
int inorder[length] = {4, 2, 8, 1, 6, 3, 7};
Test("Test7: for unmatched input", preorder, inorder, length);
}
int main()
{
Test1();
Test2();
Test3();
Test4();
Test5();
Test6();
Test7();
return 0;
}
测试用例:
- 普通二叉树(完全、不完全二叉树)
- 特殊二叉树(所有结点都没有右子结点的二叉树,所有结点都没有左子结点的二叉树,只有一个结点的二叉树)
- 特殊输入测试(二叉树根结点指针为NULL,输入的前序遍历序列和中序遍历序列不匹配)