算法训练 绘制地图
【练习时间】2021/3/6(实际上应该是3/7-3/8号都在和这个题作斗争,在崩溃的边缘不断试探……)
【题目名称】 算法训练 绘制地图
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
最近,WYF正准备参观他的点卡工厂。WYF集团的经理氰垃圾需要帮助WYF设计参“观”路线。现在,氰垃圾知道一下几件事情:
1.WYF的点卡工厂构成一颗二叉树。
2.一共有n座工厂。
3.他需要把这颗树上的点以后序遍历的方法列出来,才能绘制地图。
还好,最近他的属下给了他先序遍历和中序遍历的数据。可是,氰垃圾最近还要帮㊎澤穻解决一些问题,没有时间。请你帮帮他,替他完成这项任务。由于氰垃圾的一些特殊的要求,WYF的参观路线将会是这棵树的后序遍历。
输入格式
第一行一个整数n,表示一共又n座工厂。
第二行n个整数,表示先序遍历。
第三行n个整数,表示中序遍历。
输出格式
输出共一行,包含n个整数,为后序遍历。
样例输入
8
1 2 4 5 7 3 6 8
4 2 7 5 1 8 6 3
样例输出
4 7 5 2 8 6 3 1
数据规模和约定
0<n<100000,。保证先序遍历和中序遍历合法,且均为1~n。
【题目分析】
简而言之,就是给你前序遍历和中序遍历的数组,让你输出后序遍历的结果,请注意,此题的数据量巨大,需要对程序进行优化,降低时间复杂度。
【解题过程】
历经两天折磨的时光,翻阅大量的解题思路,终于在三月八号这天早晨成功实现,啊啊啊啊啊!!!
下面我来说一下具体的解题思路到最终的代码优化:
-
根据前序数组和中序数组,求出根节点、左子树根节点、右子树根节点;
(1)首先我们从前序数组当中读取元素,并查找其在中序数组当中的位置。
int FindNode(int num)
{
for(int i=0;i<n;i++)
{
if(mid[i]==num)
return i;
}
return -1;
}
(2)以这个位置为中心把中序数组一分为两段,左边的数都在左子树上,右边的数都在右子树上。为了便于创建节点,我们设置了一个函数。
BiNode *CreateNode(int val)
{
BiNode *node = new BiNode;
node->data=val;
node->rchild=NULL;
node->lchild=NULL;
return node;
}
(3)将这两段分别重复调用上述操作,每调用一次,前序数组当中的指向向后加一,优先调用左半段。
BiNode *CreatTree(int &cur_pos,int left,int right)//cur_pos代表指向前序数组的位置,left为中序数组的左边界,right为右边界
{
if(cur_pos>n-1||left>right)
{
if(left>right) cur_pos-=1;
return NULL;
}
if(left==right) return CreateNode(pre[cur_pos]);
int val = pre[cur_pos];
int pos = FindNode(val);
BiNode *root=CreateNode(val);
cur_pos+=1;
root->lchild=CreatTree(cur_pos,left,pos-1);
cur_pos+=1;
root->rchild=CreatTree(cur_pos,pos+1,right);
return root;
}
- 调用终止返回的条件
(1)当前序数组走到边界时,或中序数组的右边界大于左边界时(此时前序数组指针减一后再返回NULL,目的是继续对此数操作,直到找到合适的位置),返回NULL;
(2)当中序数组的左右边界相等时,他们都指向同一个数,则创建一个节点并返回。
【源代码1.0版】
#include <iostream>
using namespace std;
int n=0,pre[100000],mid[100000];
struct BiNode
{
int data;
BiNode *lchild;
BiNode *rchild;
};
int FindNode(int num)
{
for(int i=0;i<n;i++)
{
if(mid[i]==num)
return i;
}
return -1;
}
BiNode *CreateNode(int val)
{
BiNode *node = new BiNode;
node->data=val;
node->rchild=NULL;
node->lchild=NULL;
return node;
}
BiNode *CreatTree(int &cur_pos,int left,int right)
{
if(cur_pos>n-1||left>right)
{
if(left>right) cur_pos-=1;
return NULL;
}
if(left==right) return CreateNode(pre[cur_pos]);
int val = pre[cur_pos];
int pos = FindNode(val);
BiNode *root=CreateNode(val);
cur_pos+=1;
root->lchild=CreatTree(cur_pos,left,pos-1);
cur_pos+=1;
root->rchild=CreatTree(cur_pos,pos+1,right);
return root;
}
void PostOrder(BiNode *bt)
{
if(bt==NULL)
return;
else{
PostOrder(bt->lchild);
PostOrder(bt->rchild);
cout<<bt->data<<" ";
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
cin>>pre[i];
for(int i=0;i<n;i++)
cin>>mid[i];
int cur_pos=0;
BiNode *root=CreatTree(cur_pos,0,n-1);
PostOrder(root);
return 0;
}
提交后我们发现情况是这样滴……
运行超时的数据量都是十万,时间限制是1s我们超时了……没办法只能优化代码!!
【优化代码思路】
我认为造成超时的原因可能有以下几个方面:
- 在中序数组中查找元素时能不能尽量缩短查找范围?
- 有没有可能在不构建树的基础上后序输出?
现在说明以下解决上述问题的思路:
针对question1:
我们在FindNode函数中传入查找的起始位置和终止位置,起始位置就是中序数组的左边界,终止位置是中序数组的右边界。可大大缩短了查找量!
int FindNode(int num,int start,int end)//在中序数组的左右边界范围之内查找,如果有那么一定在此范围之内!!!
{
for(int i=start;i<=end;i++)
{
if(mid[i]==num)
return i;
}
return -1;
}
针对question2:
我们不再构建树也不要将数据存放到节点当中,我们直接在函数中输出数据,后序输出的思路是:左子树——>右子树——>根节点;那么我们可以在左子树和右子树遍历完成后输出数据即可。
void CreatTree(int &cur_pos,int left,int right)//函数类型更改为void
{
if(cur_pos>n-1||left>right)
{
if(left>right) cur_pos-=1;
return;//直接返回即可
}
if(left==right) //当左右边界相遇时输出此数!
{
cout<<pre[cur_pos]<<" ";
return;
}
int val = pre[cur_pos];
int pos = FindNode(val,left,right);
cur_pos+=1;
CreatTree(cur_pos,left,pos-1);//遍历左子树
cur_pos+=1;
CreatTree(cur_pos,pos+1,right);//遍历右子树
cout<<val<<" ";//输出根节点
return;
}
【源代码2.0版】
#include <iostream>
using namespace std;
int n=0,pre[100000],mid[100000];
int FindNode(int num,int start,int end)
{
for(int i=start;i<=end;i++)
{
if(mid[i]==num)
return i;
}
return -1;
}
void CreatTree(int &cur_pos,int left,int right)
{
if(cur_pos>n-1||left>right)
{
if(left>right) cur_pos-=1;
return;
}
if(left==right)
{
cout<<pre[cur_pos]<<" ";
return;
}
int val = pre[cur_pos];
int pos = FindNode(val,left,right);
cur_pos+=1;
CreatTree(cur_pos,left,pos-1);
cur_pos+=1;
CreatTree(cur_pos,pos+1,right);
cout<<val<<" ";
return;
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
cin>>pre[i];
for(int i=0;i<n;i++)
cin>>mid[i];
int cur_pos=0;
CreatTree(cur_pos,0,n-1);
return 0;
}
【运行结果】