Codeup Contest ID:100000610
PS:本节是二叉树概念的讲述,没有相关习题。
Codeup Contest ID:100000611
题目描述
小明在做数据结构的作业,其中一题是给你一棵二叉树的前序遍历和中序遍历结果,要求你写出这棵二叉树的后序遍历结果。
输入
输入包含多组测试数据。每组输入包含两个字符串,分别表示二叉树的前序遍历和中序遍历结果。每个字符串由不重复的大写字母组成。
输出
对于每组输入,输出对应的二叉树的后续遍历结果。
样例输入
DBACEGF ABCDEFG
BCAD CBAD
样例输出
ACBFGED
CDAB
思路
这题很常规,书上也有具体的代码,就是已知前序序列+中序序列,先重建二叉树,再求后序序列即可。
代码
#include
#include
#include
#include
#include
using namespace std;
struct node{
char data;//数据域是一个字母
node* lchild;
node* rchild;
};
string pre;
string in;
node* create(int preL, int preR, int inL, int inR){
if(preL>preR){
return NULL;
}
node* root = new node;
root->data = pre[preL];//确定根节点
int k;
for(k=inL;k<=inR;k++){//遍历中序序列区间
if(in[k]==pre[preL]) break;//在中序序列中找到根节点了,break
}
int numLeft = k-inL;//左子树的长度
root->lchild = create(preL+1, preL+numLeft, inL, k-1);
root->rchild = create(preL+numLeft+1, preR, k+1, inR);
return root;
}
void postorder(node* root){//后续遍历
if(root==NULL){
return;
}
postorder(root->lchild);
postorder(root->rchild);
cout<<root->data;
}
int main(){
while(cin>>pre>>in){
node* root = create(0, pre.length()-1, 0, in.length()-1);//都是闭区间
postorder(root);
cout<<'\n';
pre.clear();
in.clear();
}
return 0;
}
题目描述
如上所示,由正整数1,2,3……组成了一颗特殊二叉树。我们已知这个二叉树的最后一个结点是n。现在的问题是,结点m所在的子树中一共包括多少个结点。
比如,n = 12,m = 3那么上图中的结点13,14,15以及后面的结点都是不存在的,结点m所在子树中包括的结点有3,6,7,12,因此结点m的所在子树中共有4个结点。
输入
输入数据包括多行,每行给出一组测试数据,包括两个整数m,n (1 <= m <= n <= 1000000000)。最后一组测试数据中包括两个0,表示输入的结束,这组数据不用处理。
输出
对于每一组测试数据,输出一行,该行包含一个整数,给出结点m所在子树中包括的结点的数目。
样例输入
3 7
142 6574
2 754
0 0
样例输出
3
63
498
思路
这题我用递归来写,差点超时……为了保险起见大家还是把递归改成循环吧。
思路也很简单,按照题目的意思来说,就是给一棵完全二叉树,然后让你计算某个结点所在子树的总结点数(包括自己),因为题目中标号已经直接给出了(就是按一层一层这样子标号),那么就能运用书上的一个结论,假如说当前所在结点的标号是x,那它的左孩子就是2x,它的右孩子就是2x+1。
于是这个问题就可以用dfs去解决了:只要存在左孩子,就往那个方向递归;只要存在右孩子,就往那个方向递归。递归边界就是:如果算出来的2x或者2x+1已经大于给出的最后一个结点n,那就说明对应的左孩子或者右孩子不存在,return就好了。
代码
#include
#include
#include
#include
using namespace std;
void cnt(int m, int n, int &sum){//m是要求的结点,n是树的最后一个结点,sum是所求的结点m的子树包含的总结点
if(m>n) return ;
if(2*m<=n){
sum++;
cnt(2*m, n, sum);
}
if(2*m+1<=n){
sum++;
cnt(2*m+1, n, sum);
}
}
int main(){
int m, n;
while(scanf("%d%d", &m, &n) != EOF){
if(m==0&&n==0) break;
int sum = 1;//先算上结点本身
cnt(m, n, sum);
printf("%d\n", sum);
}
return 0;
}
题目描述
二叉树的前序、中序、后序遍历的定义:
前序遍历:对任一子树,先访问跟,然后遍历其左子树,最后遍历其右子树;
中序遍历:对任一子树,先遍历其左子树,然后访问根,最后遍历其右子树;
后序遍历:对任一子树,先遍历其左子树,然后遍历其右子树,最后访问根。
给定一棵二叉树的前序遍历和中序遍历,求其后序遍历(提示:给定前序遍历与中序遍历能够唯一确定后序遍历)。
输入
两个字符串,其长度n均小于等于26。
第一行为前序遍历,第二行为中序遍历。
二叉树中的结点名称以大写字母表示:A,B,C…最多26个结点。
输出
输入样例可能有多组,对于每组测试样例,
输出一行,为后序遍历的字符串。
样例输入
ABC
CBA
ABCDEFG
DCBAEFG
样例输出
CBA
DCBGFEA
思路
这题和问题A一样,建议尽量自己独立写出重建二叉树和后序遍历的函数,毕竟考试的时候可不能看书呀~
代码
#include
#include
#include
#include
#include
using namespace std;
struct node{
char data;
node* lchild;
node* rchild;
};
string pre;
string in;
node* create(int preL, int preR, int inL, int inR){
if(preL>preR) return NULL;
node* root = new node;
root->data = pre[preL];
int k;
for(k=inL;k<=inR;k++){
if(in[k]==pre[preL]) break;
}
int numLeft = k-inL;
root->lchild = create(preL+1, preL+numLeft, inL, k-1);
root->rchild = create(preL+numLeft+1, preR, k+1, inR);
return root;
}
void postorder(node* root){
if(root==NULL) return;
postorder(root->lchild);
postorder(root->rchild);
cout<<root->data;
}
int main(){
while(cin>>pre>>in){
node* root = create(0, pre.length()-1, 0, in.length()-1);
postorder(root);
cout<<'\n';
pre.clear();
in.clear();
}
return 0;
}
题目描述
编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。
例如如下的先序遍历字符串:
ABC##DE#G##F###
其中“#”表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。
输入
输入包括1行字符串,长度不超过100。
输出
可能有多组测试数据,对于每组数据,
输出将输入字符串建立二叉树后中序遍历的序列,每个字符后面都有一个空格。
每个输出结果占一行。
样例输入
a#b#cdef#####
a##
样例输出
a b f e d c
a
思路
这题因为自己的蠢卡了一段时间o(╥﹏╥)o,其实思路非常简单,我一开始就想到了,但是中序的输出一直不对,然后就改了书上的那种左右子树区间的写法来建树,结果一直卡在答案错误50。后来看了大佬【小魚兒.】的写法,结果发现居然跟我一开始的想法差不多,于是我就用单步调试去比较了一下才找到问题所在……
那么来说一下这题的思路,如果把样例在纸上画一画,就会发现,这通过先序序列建树的方法根本就是DFS嘛,先往左子树的方向建树,“死胡同”就是碰到字符"#",然后返回上一层,再往右子树的方向建。这里特别容易出错的一点是,千万不要在函数参数里写当前序列位置index!!因为我们在手写建树的时候,总是读字符串下一位来判断的,如果下一位是“#”,那么返回上一层,往另一个方向去填“#”的下一位,以样例来说,因为第一位不是“#”,说明不是空树,那么读入a是根,然后往它的左子树方向走,读下一位,下一位是“#”,是空树,说明a的左子树是空的,返回上一层,然后再往a的右子树方向走,于是读入“b”(如果函数参数里面写index的话,那么返回a的那一层,准备往a的右子树方向走的时候,它的index依然是a的下一位,即“#”)。
代码
#include
#include
#include
#include
#include
using namespace std;
struct node{
char data;
node* lchild;
node* rchild;
};
string pre;
int index;//要用全局的
node* create(){//index不要写在函数里
if(pre[index]=='#'){
index++;
return NULL;
}
node* root = new node;
root->data = pre[index];
index++;
root->lchild = create();
root->rchild = create();//否则左子树建立完回退到右子树这里的话,index也会回退
//参照dfs,如果index是函数参数,那么会回退到上一个岔路口的状态,所以index会回退
//但是我们需要的是index一直从字符串左端往右走
return root;
}
void inorder(node* root){
if(root==NULL) return;
inorder(root->lchild);
cout<<root->data<<' ';//每个字符后面都有一个空格
inorder(root->rchild);
}
int main(){
while(cin>>pre){
index = 0;
node* root = create();
inorder(root);
cout<<'\n';
pre.clear();
}
return 0;
}
二叉树的遍历其他几题都很简单(明明是太常规了书上都有现成的代码),最后一题稍微卡了一下,参考了大佬们的题解并通过自己的单步调试发现了问题所在,可以说是对DFS有了更深刻的理解(递归返回上一层之后,所有状态都会回溯,就像回到过去一样hhh),所以要是想像最后一题那样对字符串一路从头读到尾而不回退的话,还是用全局变量叭~