剑指Offer算法题之已知两种遍历方式重建二叉树--面试题6:重建二叉树
2017-07-31 22:10
411 查看
直接上题目
本题所构建的二叉树如下图
-本文已知二叉树的前序遍历和中序遍历,在二叉树的前序遍历序列中,第一个数字总是树的根结点的值。但在中序遍历序列中,根结点的值再序列的中间,所以 左子树的结点的值位于根结点的值得左边,而右子树的结点的值位于根结点的值得右边。因此可知,我们需要扫描一遍中序遍历,找到根结点。
如下图可知,前序遍历序列的第一个数字1就是根结点,进过扫描中序遍历后找到根结点1所在位置。因此前面3个数字就是左子树结点值,位于1后面的数字都是右子树结点的值。
由于在中序遍历序列中,有3个数字是左子树结点的值,因此左子树总共有3个左子树结点。同样,在前序遍历的根结点后,也应该有3个结点从属于左子树,后面所有的结点从属于右子树。这样就分别找到了左右子树对应的子序列。
既然我们已经找到了中序遍历和前序遍历的左右子树,我们可以用相同的方法分别取构建左右子树。简单说就是用递归的方法去完整整个二叉树的重建。
代码编写思路与注意点:
1、根据题干可得,本题是通过前序遍历和中序遍历重建二叉树的。因此怎么由前序遍历和中序遍历获得后序遍历是关键。–根结点,是本题的突破口。在中序遍历序列中找到根结点是算法的第一步。
2、因为题干的要求是输出根结点,无疑需要为根结点申请空间。
3、找到根结点的中序遍历,很清晰得出左子树和右子树的长度。通过这个长度也得出左子树和右子树的前序遍历。为了避免错误,可以列出每个子树头尾结点的位置。
4、注意一个地方:尾结点=头结点+长度-1;,可以举例证实。
5、使用递归继续求得左子树和右子树的根结点,并连接成二叉树。注意:需要判断左子树和右子树是否存在。
6、注意特殊情况:1)当二叉树就只有一个结点时,直接输出根即可 2)当输入两个遍历出现问题时,直接报错:throw exception(“Invalid input”);
7、非常重要,因为输入案例是:长度,前序和中序,而算法的形参是指针,因此需要重写一个模块,把输入数据转化为实参传入重建二叉树模块。
8、根据重建的二叉树,按照每种遍历规则,输出序列。
完整代码如下
测试结果如下:
本题所构建的二叉树如下图
-本文已知二叉树的前序遍历和中序遍历,在二叉树的前序遍历序列中,第一个数字总是树的根结点的值。但在中序遍历序列中,根结点的值再序列的中间,所以 左子树的结点的值位于根结点的值得左边,而右子树的结点的值位于根结点的值得右边。因此可知,我们需要扫描一遍中序遍历,找到根结点。
如下图可知,前序遍历序列的第一个数字1就是根结点,进过扫描中序遍历后找到根结点1所在位置。因此前面3个数字就是左子树结点值,位于1后面的数字都是右子树结点的值。
由于在中序遍历序列中,有3个数字是左子树结点的值,因此左子树总共有3个左子树结点。同样,在前序遍历的根结点后,也应该有3个结点从属于左子树,后面所有的结点从属于右子树。这样就分别找到了左右子树对应的子序列。
既然我们已经找到了中序遍历和前序遍历的左右子树,我们可以用相同的方法分别取构建左右子树。简单说就是用递归的方法去完整整个二叉树的重建。
代码编写思路与注意点:
1、根据题干可得,本题是通过前序遍历和中序遍历重建二叉树的。因此怎么由前序遍历和中序遍历获得后序遍历是关键。–根结点,是本题的突破口。在中序遍历序列中找到根结点是算法的第一步。
2、因为题干的要求是输出根结点,无疑需要为根结点申请空间。
3、找到根结点的中序遍历,很清晰得出左子树和右子树的长度。通过这个长度也得出左子树和右子树的前序遍历。为了避免错误,可以列出每个子树头尾结点的位置。
4、注意一个地方:尾结点=头结点+长度-1;,可以举例证实。
5、使用递归继续求得左子树和右子树的根结点,并连接成二叉树。注意:需要判断左子树和右子树是否存在。
6、注意特殊情况:1)当二叉树就只有一个结点时,直接输出根即可 2)当输入两个遍历出现问题时,直接报错:throw exception(“Invalid input”);
7、非常重要,因为输入案例是:长度,前序和中序,而算法的形参是指针,因此需要重写一个模块,把输入数据转化为实参传入重建二叉树模块。
8、根据重建的二叉树,按照每种遍历规则,输出序列。
完整代码如下
#include "stdafx.h" #include <iostream> using namespace std; //构建二叉树的结点 struct BinaryTreeNode { int value; BinaryTreeNode* pLeft; BinaryTreeNode* pRight; }; BinaryTreeNode* Construct(int* preorder,int* inorder,int length); //本题二叉树的核心算法 BinaryTreeNode* ConstructCore(int* startPreorder,int* endPreorder,int* startInorder,int* endInorder) { //前序遍历的第一个结点就是根结点 int rootValue =startPreorder[0]; BinaryTreeNode* root=new BinaryTreeNode(); //赋值,注意给左右孩子指针初始化 root->value=rootValue; root->pLeft = root->pRight = NULL;//左子树和右子树需要初始化 //先处理特殊情况,也就是树只剩一个结点的情况 if(startPreorder==endPreorder) { if (startInorder==endInorder) return root; else throw exception("Invalid input"); } //在中序遍历序列中找到根结点的值 int* rootInorder= startInorder; while(*rootInorder!=rootValue&&rootInorder<=endInorder) { rootInorder++; } //找到根结点位置后,分别做出左右子树,及长度 int leftLength=rootInorder-startInorder; //int rightLength=endInorder-rootInorder; //左子树的中序遍历 int* leftstartInorder=startInorder; int* leftendInorder=rootInorder-1; //右子树的中序遍历 int* rightstartInorder=rootInorder+1; int* rightendInorder=endInorder; //左子树的前序遍历 int* leftstartPreorder=startPreorder+1; int* leftendPreorder=startPreorder+leftLength;//注意 //右子树的前序遍历 int* rightstartPreorder=leftendPreorder+1; int* rightendPreorder=endPreorder; //构建左子树--左子树长度>0 if(leftLength>0) { root->pLeft=ConstructCore(leftstartPreorder,leftendPreorder,leftstartInorder,leftendInorder); } //构建右子树-- if((endInorder-rootInorder)>0) { root->pRight=ConstructCore(rightstartPreorder,rightendPreorder,rightstartInorder,rightendInorder); } return root; } //由输入案例转化到核心算法的模块 BinaryTreeNode* Construct(int* preorder,int* inorder,int length) { if(preorder==NULL||inorder==NULL||length<=0) return NULL; else return ConstructCore(preorder,preorder+length-1,inorder,inorder+length-1); } //二叉树的前序遍历 void PreorderTraverse(BinaryTreeNode* pNode) { if(pNode==NULL) return ; cout<<pNode->value<<" "; PreorderTraverse(pNode->pLeft); PreorderTraverse(pNode->pRight); } //中序遍历 void InorderTraverse(BinaryTreeNode* pNode) { if(pNode==NULL) return ; PreorderTraverse(pNode->pLeft); cout<<pNode->value<<" "; PreorderTraverse(pNode->pRight); } //后序遍历 void LastorderTraverse(BinaryTreeNode* pNode) { if(pNode==NULL) return ; PreorderTraverse(pNode->pLeft); PreorderTraverse(pNode->pRight); cout<<pNode->value<<" "; } //调用测试模块 void Test(int* preorder,int* inorder,int length) { BinaryTreeNode* root=Construct(preorder,inorder,length); printf("The preorder sequence is: \n"); PreorderTraverse(root); cout<<endl; printf("The inorder sequence is:\n "); InorderTraverse(root); cout<<endl; printf("The Lastorder sequence is:\n "); LastorderTraverse(root); cout<<endl; } //输入测试案例 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(preorder,inorder,length); } // 普通二叉树 // 1 // / \ // 2 3 // / / \ // 4 5 6 // \ / // 7 8 int main() { Test1(); system("pause"); return 0; }
测试结果如下:
相关文章推荐
- 已知前序中序,重建二叉树(加7种遍历的方式)
- 二叉树面试题--已知二叉树的两种遍历序列,求出另一种遍历序列
- 二叉树面试题--已知二叉树的两种遍历序列,求出另一种遍历序列
- 已知某二叉树的某两种遍历序列,求另一种遍历序列面试题解法总结(转)
- 已知二叉树的两种遍历序列重建树
- 二叉树面试题--已知二叉树的两种遍历序列,求出另一种遍历序列
- 已知二叉树的前序遍历和中序遍历重建二叉树(二叉树)
- 【面试算法系列】已知二叉树的前序和中序遍历重建二叉树 - C语言实现
- 面试题6:重建二叉树(前序遍历和中序遍历)
- 二叉树的遍历(前序 中序 后序)和已知两种遍历顺序求另外一种遍历顺序(含实例)
- 已知二叉树的前序遍历和中序遍历的结果,重建二叉树
- 【面试题】剑指Offer-6-根据前序和中序遍历重建二叉树
- 已知前序遍历和中序遍历,重建二叉树
- 已知二叉树的两种遍历序列,求出另一种遍历序列
- 数据结构与算法学习之二叉树的遍历:已知先序、中序、后序序列三者中的两种求另外一种遍历的算法
- 二叉树(14)----由前序遍历和中序遍历重建二叉树,递归方式
- 二叉树 已知两种遍历恢复二叉树
- 已知两种二叉树的遍历,求第三种
- 二叉树:由已知两种遍历求未知遍历
- 二叉树的非递归前序、中序、后序遍历(两种方式的非递归后序遍历)