重建二叉树
题目
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树,假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
typedef struct BinaryTreeNode{
int value;
BinaryTreeNode* left;
BinaryTreeNode* right;
BinaryTreeNode():value(0),left(NULL),right(NULL){};
}BinaryTreeNode;
- 前序序列遍历确定root
- 由root去将中序遍历序列一分为二,左边leftLength个,右边rightLength个
- 则左子树的前序遍历序列,中序遍历序列确定下来的;右子树的前序遍历序列,中序遍历序列也确定下来了。
- 递归
代码示例(不太推荐这种写法,直接看下面的示例)
-------------------------------------------------------------------------------来自《剑指offer》
#include<iostream>
#include<exception>
#include<stdexcept>
using namespace std;
typedef struct BinaryTreeNode{
int value;
BinaryTreeNode* left;
BinaryTreeNode* right;
BinaryTreeNode():value(0),left(NULL),right(NULL){};
}BinaryTreeNode;
//声明
BinaryTreeNode* ConstructCore(int *startPreorder,int *endPreorder,int *startInorder,int *endInorder);
//重建二叉树
BinaryTreeNode* Construct(int * preorder,int* inorder,int length){
if(preorder==NULL||inorder==NULL||length<=0) return NULL;
return ConstructCore(preorder,preorder+length-1,inorder,inorder+length-1);
}
BinaryTreeNode* ConstructCore(int *startPreorder,int *endPreorder,int *startInorder,int *endInorder){
int rootValue=startPreorder[0];
BinaryTreeNode* root=new BinaryTreeNode();
root->value=rootValue;
//base case
if(startPreorder==endPreorder){
//仅有一个节点
if(startInorder==endInorder && *startPreorder==*startInorder)
return root;
else
//注意参考https://stackoverflow.com/questions/28640553/exception-class-with-a-char-constructor
//windows的msvc编译器中可以使用throw std::exception("Invalid input")
//gcc编译器中,不能这样throw std::exception("Invalid input")
throw std::logic_error("Invalid input");
}
//找到rootValue在Inorder序列中的位置
int *rootInorder=startInorder;
while(rootInorder<=endInorder&&*rootInorder!=rootValue)
rootInorder++;
if(rootInorder>endInorder)
throw std::logic_error("Invalid input");
//左子树序列的长度
int leftLength=rootInorder-startInorder;
int *leftPreorderEnd=startPreorder+leftLength;
if(leftLength>0)//左子树序列有值
{
root->left=ConstructCore(startPreorder+1,leftPreorderEnd,startInorder,rootInorder-1);
}
if(leftLength<endPreorder-startPreorder){//右子树序列有值
root->right=ConstructCore(leftPreorderEnd+1,endPreorder,rootInorder+1,endInorder);
}
return root;
}
//后序遍历二叉树作为测试
void postOrder(BinaryTreeNode* root){
if(root==NULL) return ;
postOrder(root->left);
postOrder(root->right);
cout<<root->value<<"-> ";
}
int main(){
//动态数组初始化
int* preOrder=new int[8]{1,2,4,7,3,5,8,6};
int* InOrder=new int[8]{1,2,4,7,3,5,8,6};
BinaryTreeNode* root=Construct(preOrder,InOrder,8);
postOrder(root);
delete[] preOrder;
delete[] InOrder;
system("pause");
return 0;
}
上面是剑指offer的代码示例,感觉不太好,下面推荐的是leetcode的代码示例
105. 从前序与中序遍历序列构造二叉树
思路:
前序:根 左子树节点元素 右子树节点元素
中序:左子树节点元素 根 右子树节点元素
1.先由前序确定根,然后由根元素去中序序列中查找,进而将中序序列分成两半,左边leftNums个节点,右边rightNums个节点
2.然后开始递归
注意:这里由根元素去中序序列中查找,可以中unordered_map优化,先把中序的值和对应下标存在unordered_map中
#include<iostream>
#include<vector>
#include<unordered_map>
using namespace std;
typedef struct BinaryTreeNode{
int value;
BinaryTreeNode* left;
BinaryTreeNode* right;
BinaryTreeNode(int val):value(val),left(NULL),right(NULL){};
}BinaryTreeNode;
BinaryTreeNode* constructBTCore(vector<int>& preOrder,vector<int>& inOrder,int preLeft,int preRight,int inLeft,int inRight,unordered_map<int,int>& mp){
if(preRight<preLeft||inRight<inLeft) return nullptr;
if(preRight-preLeft!=inRight-inLeft) return nullptr;
//建立根节点
BinaryTreeNode* root=new BinaryTreeNode(preOrder[preLeft]);
//找到根在中序序列中的位置
int leftNums=mp[preOrder[preLeft]]-inLeft;
//重建左边
root->left=constructBTCore(preOrder,inOrder,preLeft+1,preLeft+leftNums,inLeft,inLeft+leftNums-1,mp);
//重建右边
root->right=constructBTCore(preOrder,inOrder,preLeft+leftNums+1,preRight,mp[preOrder[preLeft]]+1,inRight,mp);
return root;
}
BinaryTreeNode* constructBT(vector<int>& preOrder,vector<int>& inOrder){
if(preOrder.size()!=inOrder.size()) return nullptr;
//遍历inOrder,保存到mp
unordered_map<int,int> mp;
for(int i=0;i<inOrder.size();i++){
mp[inOrder[i]]=i;
}
return constructBTCore(preOrder,inOrder,0,preOrder.size()-1,0,inOrder.size()-1,mp);
}
二叉树的下一个节点
题目
给定一棵二叉树和其中的一个节点,如何找出中序遍历序列的下一个节点?树中的节点除了有两个分别指向左、右子节点的指针,还有一个指向父节点的指针。
- 如果一个节点有右子树,则它的下一个节点就是其右子树的最左节点
- 如果一个节点没有右子树,且它是它父亲节点的左子节点,则它的下一个节点就是其父亲节点
- 如果一个节点没有右子树,且它是它父亲节点的右子节点,则我们可以沿着指向父亲节点的指针一直向上遍历,直到找到一个是它父亲节点左子节点的节点。如果这样的节点存在,那么这个节点的父亲节点就是我们要找的下一个节点。
#include<iostream>
#include<exception>
#include<stdexcept>
using namespace std;
typedef struct BinaryTreeNode{
int value;
BinaryTreeNode* left;
BinaryTreeNode* right;
BinaryTreeNode* parent;
BinaryTreeNode():value(0),left(NULL),right(NULL),parent(NULL){};
}BinaryTreeNode;
/*
*1. 如果一个节点有右子树,则它的下一个节点就是其右子树的最左节点
*2. 如果一个节点没有右子树,且它是它父亲节点的左子节点,则它的下一个节点就是其父亲节点
*3. 如果一个节点没有右子树,且它是它父亲节点的右子节点,则我们可以沿着指向父亲节点的指针一直向上遍历,
* 直到找到一个是它父亲节点左子节点的节点。如果这样的节点存在,那么这个节点的父亲节点就是我们要找的下一个节点。
*/
BinaryTreeNode* GetNextNode(BinaryTreeNode* pNode){
if(pNode==NULL) return NULL;
BinaryTreeNode* NextNode=NULL;
if(pNode->right!=NULL) {
NextNode=pNode->right;
while(NextNode->left!=NULL)
NextNode=NextNode->left;
return NextNode;
}
else if(pNode->parent!=NULL){
if(pNode->parent->left==pNode)
return pNode->parent;
else{
BinaryTreeNode* CurrentNode=pNode;
while(CurrentNode->parent!=NULL && CurrentNode->parent->right==CurrentNode)
CurrentNode=CurrentNode->parent;
if(CurrentNode->parent!=NULL)//说明存在下一个节点
return CurrentNode->parent;
}
}
//说明已回溯到根节点,也没有找到下一个节点。
return NULL;
}
int main(){
BinaryTreeNode array[5];//对象数组,默认初始化
for(int i=0;i<5;i++){
array[i].value=i;
}
array[2].parent=NULL;
array[2].left=&array[1];
array[2].right=&array[4];
array[1].parent=&array[2];
array[1].left=&array[0];
array[1].right=NULL;
array[0].parent=&array[1];
array[0].left=NULL;
array[0].right=NULL;
array[4].parent=&array[2];
array[4].left=&array[3];
array[4].right=NULL;
array[3].parent=&array[4];
array[3].left=NULL;
array[3].right=NULL;
//测试
for(int j=0;j<5;j++){
BinaryTreeNode* temp=GetNextNode(&array[j]);
if(temp!=NULL)
cout<<"array["<<j<<"]"<<"->NextNode->value="<<temp->value<<endl;
else
cout<<"array["<<j<<"]"<<"->NextNode==NULL"<<endl;
}
system("pause");
return 0;
}
测试时建立的二叉树如下图:
输出结果如下: