题目描述
二哥学了二叉树的顺序存储后,被下面一个问题难住了,于是他请你帮他解决。给你一个前序遍历和中序遍历,问顺序存储的数组是什么样子的。
输入格式
第一行为前序遍历,第二行为中序遍历,节点个数不超过26.
输出格式
输出一行,表示顺序存储的数组,以空格隔开,NULL
表示空节点,数组空间不超过1000个节点.
样例输入
ABCD BADC
样例输出
A B C NULL NULL D
数据范围
(无)
题目分析
你有没有发现SJTU的OJ有一个二哥还有一个小可怜经常出题?二哥摘苹果,二哥种花生,二哥养兔子……
这个题目需要的知识有函数的递归,二叉树的基本概念,前序遍历、中序遍历。如果没有学过可以看看数据结构的教材《数据结构:思想与实现(翁慧玉 俞勇)》- 第六章 树。只需要知道概念即可,可以看一下教材的P162-164,看完你就会做这个题目(确信)。
程序设计的课程就学二叉树ACM班也太猛了吧! 不过我想趁这个题目复习一下字符串的知识,还有了解一下二叉树的基本知识。
二叉树
前序遍历: 先访问根节点,然后前序遍历左子树,前序遍历右子树
中序遍历: 先中序访问左子树,再访问根节点,最后中序遍历右子树
后序遍历: 先中序访问左子树,再中序遍历右子树,最后访问根节点
一句话总结:前序、中序、后序指的是 根节点 在访问顺序的排序!
编号问题: 计算公式如下 (注意:树的根结点编号为 1)
右
儿
子
编
号
=
根
节
点
编
号
×
2
右儿子编号=根节点编号\times2
右儿子编号=根节点编号×2
左
儿
子
编
号
=
根
节
点
编
号
×
2
+
1
左儿子编号=根节点编号\times2+1
左儿子编号=根节点编号×2+1
字符串
(转载)字符串知识传送门,想看原版的可以走传送门,不想走的可以接着往下看,内容差不多。个人觉得要用的时候看也可,反正不是经常用的玩野。
读取方法:
编号 | 读取方法 | 读取特点 |
---|---|---|
方法一 | cin | 接受一个字符串,遇“空格”、“Tab”、“回车”都结束 |
方法二 | cin.get() | 用法一:cin.get(字符变量名)可以用来接收字符 用法二:cin.get(字符数组名,接收字符数)用来接收一行字符串,可以接收空格 用法三:cin.get(无参数)没有参数主要是用于舍弃输入流中的不需要的字符,或者舍弃回车,弥补cin.get(字符数组名,接收字符数目)的不足。 |
方法三 | cin.getline() | cin.getline() ----接受一个字符串,可以接收空格并输出 |
方法四 | getline() | getline() // 接受一个字符串,可以接收空格并输出,需包含#include<string> |
方法五 | gets() | gets()// 接受一个字符串,可以接收空格并输出,需包含#include<string> |
方法六 | getchar() | getchar()//接受一个字符 |
长度获取:
- 加上头文件:
#include <string.h>
- 定义一个字符数组
char pre[50];
- 输入数据的时候可以这样
cin >> pre;
strlen(数组名);
函数将会返回一个整数,表示一个字符数组内字符个数
题目解答
老套路,从我们之前写的就可以看出来:
前序遍历: 先访问根节点,然后前序遍历左子树,前序遍历右子树
前序遍历里面套娃前序遍历,明显是要递归解决!
递归的核心:函数之间的通讯内容 [传递的信息] ,终止条件。
我们不妨还是用课本的案例来:
课本案例:
已知一个二叉树的前序遍历,中序遍历,求这个二叉树:
二叉树前序遍历序列是:A、L、B、E、C、D、W、X
二叉树中序遍历序列是:B、L、E、A、C、W、X、D
- 由前序遍历序列找根节点,显然是 A
- 由中序遍历划分根节点左边的元素、右边的元素(具体方法:在中序遍历中找根节点A,根节点A左边的就是 根节点的左子树里面的元素,右边的是 根节点的右子树里面的元素)
- 套娃循环,分别对 根节点的左子树里面的元素 、 根节点的右子树里面的元素 重复递归,执行步骤一、二,直到每个元素位置确定!
回归话题:函数之间的通讯内容有:节点的编号、当前查找的序列在前序遍历数组的起点、终点、在中序遍历的起点、终点,终止条件:当前查找的元素只有一个!此时就要确定位置,位置就是当前节点的编号,上代码!
#include <iostream>
#include <string.h>
using namespace std;
char pre[50]; // 前序遍历序列
char in[50]; // 中序遍历序列
char ans[1050]; // 答案数组
// 把中序遍历分割为左右两半
// 使用方法(0,num-1,0,num-1,1);
void solution(int prest,int preed,int inst,int ined,int rootnum)
{
ans[rootnum] = pre[prest]; // 把根节点传入答案数组
// 当前查找的元素只有一个!此时就要向答案数组里面传递信息,位置就是当前节点的编号 rootnum
if (prest == preed && inst == ined)
{
ans[rootnum] = pre[prest];
return;
}
// 当前查找的元素超过一个,继续二分!
else
{
int targetnum;
for (int i = inst; i <= ined; i++)
{
if (in[i] == pre[prest])
{
targetnum = i;
break;
}
// 在中序遍历中找根节点A,便于后续划分
}
if (targetnum > inst)
// 下面这个函数递归的是左子树的元素 2 * rootnum是左儿子的编号
solution(prest + 1, prest + targetnum - inst, inst, targetnum - 1, 2 * rootnum);
if (targetnum < ined)
// 下面这个函数递归的是右子树的元素 2 * rootnum + 1是右儿子的编号
solution(preed - ined + targetnum + 1, preed, targetnum + 1, ined, 2 * rootnum + 1);
}
}
int main()
{
cin >> pre;
cin >> in;
int num = strlen(pre); // 获取字符串的长度,也可以用下面的注释的方法
// while (pre[num] >= 'A' && pre[num]<='Z')
// {
// num++;
// }
// 此时num反映了输入的字符串个数!
solution(0, num - 1, 0, num - 1, 1);
// 去掉答案数组里无效的元素!
int p = 1049;
while (ans[p] < 'A' || ans[p] > 'Z')
{
p--;
}
// 输出答案!
for (int i = 1; i <= p;i++)
{
if (ans[i] >= 'A' && ans[i] <= 'Z')
cout << ans[i] << " ";
else
cout << "NULL" << " ";
}
system("pause");
return 0;
}
一些其他的解法,可以参考,来源SJTU 解答集 OJ,方法各有千秋。
/* 二哥学二叉树 */
#include <iostream>
#include <cstring>
using namespace std;
char a[1005];
char dlr[30], ldr[30];
void restoreTree(char *dlr, char *ldr, int len, int root){
if (len <= 0)
return;
char r = dlr[0];
a[root] = r;
int i = 0;
while (ldr[i] != r)
++i;
restoreTree(dlr + 1, ldr, i, root * 2);
restoreTree(dlr + i + 1, ldr + i + 1, len - i - 1, root * 2 + 1);
}
int main(){
cin >> dlr >> ldr;
for (int i = 1; i <= 1004; ++i)
a[i] = ' ';
restoreTree(dlr, ldr, strlen(dlr), 1);
int n = 1000;
while (a[n] == ' ')
--n;
for (int i = 1; i <= n; ++i){
if (a[i] == ' ')
cout << "NULL ";
else
cout << a[i] << ' ';
}
cout << endl;
return 0;
}
#include "iostream"
#include "cmath"
#include "cstring"
using namespace std;
//二叉树类
template <class T>
class myBinaryTree {
public:
class node {
public:
T data;
T *lchild=nullptr, *rchild=nullptr;
};
node *root=nullptr;
//清空
void clear() {
clear(root);
}
void clear(node *p) {
if (p == nullptr)
return;
clear(p->lchild);
clear(p->rchild);
delete p;
p = nullptr;
}
//构造
myBinaryTree(){}
//析构
~myBinaryTree() {
clear(root);
}
//判断是否非空
bool empty() {
return root == nullptr;
}
};
char tdata[1009] = { 0 };
int dataSize = 0;
void createTree(int rootNum, char *tempData1,char *tempData2) {
if (strlen(tempData1) == 0) return;
char root = tempData1[0];
tdata[rootNum] = root;
dataSize = rootNum < dataSize ? dataSize : rootNum;
char ldata1[50] = { 0 }, rdata1[50] = { 0 }, ldata2[50] = { 0 }, rdata2[50] = { 0 };
int length = strlen(tempData1);
bool flag = false;
for (int i = 0,k=0; i < length; i++,k++) {
if (tempData2[i] == root) {
flag = true;
k = -1;
}
else if (!flag) {
ldata1[k] = tempData1[i + 1];
ldata2[k] = tempData2[i];
}
else if (flag) {
rdata1[k] = tempData1[i];
rdata2[k] = tempData2[i];
}
}
//递归
createTree(rootNum * 2, ldata1, ldata2);
createTree(rootNum * 2 + 1, rdata1, rdata2);
}
int main() {
char preData[50], postData[50];
cin >> preData >> postData;
createTree(1, preData, postData);
//输出
cout << tdata[1];
for (int i = 2; i <= dataSize; i++) {
if (tdata[i] == 0)
cout << " NULL";
else
cout << " " << tdata[i];
}
return 0;
}
#include <iostream>
#include <cstring>
using namespace std;
char pre[100],in[100],ans[2000];
int maxn;
void dfs(int ps,int pe,int is,int it,int p)
{
int root,leftlen,rightlen;
for (root=is;root<it;++root){
if (in[root]==pre[ps])
break;
}
ans[p]=in[root];
if (p>maxn) maxn=p;
leftlen=root-is;
if (leftlen>0) dfs(ps+1,ps+leftlen,is,root,p*2);
rightlen=it-root-1;
if (rightlen>0) dfs(ps+leftlen+1,pe,root+1,it,p*2+1);
}
int main() {
cin>>pre;
cin>>in;
dfs(0,strlen(pre),0,strlen(in),1);
for (int i=1;i<=maxn;++i)
if (ans[i]) cout<<ans[i]<<" ";
else cout<<"NULL ";
return 0;
}