1.树是递归定义的。
2.一棵树至少有1个结点(根节点)。
3.一个结点的子树个数,称为这个结点的度。
4.在用图形表示的树型结构中,对两个用线段(称为树枝)连接的相关联的结点,称上端结点为下端结点的父结点,称下端结点为上端结点的子结点。称同一个父结点的多个子结点为兄弟结点。
5.定义一棵树的根节点层次为0,其他结点的层次等于它的父结点层次加一。
6.对于树中任意两个不同的结点,如果从一个结点出发,自上而下沿着树中连着结点的线段能到达另一结点,称他们之间存在着一条路径。
7.森林是m(m>=0)棵互不相交的树的集合。
树的存储结构
父亲表示法(容易找到孩子)
const int m=10; //树的结点数
struct node
{
int data,parent; //数据域,指针域
};
node tree[m];
孩子表示法(树型单链表结构)(只能从父结点遍历子结点,不能从子结点返回到他的父结点)
const int m=10; //树的度
typedef struct node;
typedef node *tree;
struct node
{
char data; //数据域
tree child[m]; //指针域,指向若干孩子结点
};
tree t;
父亲孩子表示法(树型双链表结构)
const int m=10; //树的度
typedef struct node;
typedef node *tree; //声明tree是指向node的指针类型
struct node
{
char data; //数据域
tree child[m]; //指针域,指向若干孩子结点
tree father; //指针域,指向父亲结点
};
tree t;
孩子兄弟表示法(二叉树型表示法)
typedef struct node;
typedef node *tree;
struct node
{
char data; //数据域
tree firstchild,next; //指针域,分别指向第一个孩子结点和下一个兄弟结点
};
tree t;
1336:【例3-1】找树根和孩子
【题目描述】
给定一棵树,输出树的根rootroot,孩子最多的结点maxmax以及他的孩子。
【输入】
第一行:nn(结点个数≤100≤100),mm(边数≤200≤200)。
以下mm行:每行两个结点xx和yy,表示yy是xx的孩子(x,y≤1000x,y≤1000)。
【输出】
第一行:树根:rootroot;
第二行:孩子最多的结点maxmax;
第三行:maxmax的孩子(按编号由小到输出)。
【输入样例】
8 7
4 1
4 2
1 3
1 5
2 6
2 7
2 8
【输出样例】
4
2
6 7 8
#include<bits/stdc++.h>
using namespace std;
int tree[101]={0};
int main()
{
int n,m;
cin>>n>>m;
int root,maxn;
int x,y;
for(int i=0;i<m;i++)
{
cin>>x>>y;
tree[y]=x;
}
for(int i=1;i<=n;i++)
{
if(tree[i]==0)
{
root=i;
break;
}
}
int flag=0;
maxn=0;
int maxroot;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(tree[j]==i)
{
flag++;
}
}
if(flag>maxn)
{
maxn=flag;
maxroot=i;
}
flag=0;
}
cout<<root<<endl;
cout<<maxroot<<endl;
int f=0;
for(int i=1;i<=n;i++)
{
if(tree[i]==maxroot)
{
if(f)
cout<<" ";
cout<<i;
f++;
}
}
return 0;
}
1337:【例3-2】单词查找树
【题目描述】
在进行文法分析的时候,通常需要检测一个单词是否在我们的单词列表里。为了提高查找和定位的速度,通常都画出与单词列表所对应的单词查找树,其特点如下:
1.根结点不包含字母,除根结点外每一个结点都仅包含一个大写英文字母;
2.从根结点到某一结点,路径上经过的字母依次连起来所构成的字母序列,称为该结点对应的单词。单词列表中的每个单词,都是该单词查找树某个结点所对应的单词;
3.在满足上述条件下,该单词查找树的结点数最少。
4.例如图3-2左边的单词列表就对应于右边的单词查找树。注意,对一个确定的单词列表,请统计对应的单词查找树的结点数(包含根结点)。
【输入】
为一个单词列表,每一行仅包含一个单词和一个换行/回车符。每个单词仅由大写的英文字母组成,长度不超过63个字母 。文件总长度不超过32K,至少有一行数据。
【输出】
仅包含一个整数,该整数为单词列表对应的单词查找树的结点数。
【输入样例】
A
AN
ASP
AS
ASC
ASCII
BAS
BASIC
【输出样例】
13
#include<bits/stdc++.h>
using namespace std;
string a[10000];
string s;
int n;
int main()
{
//freopen("int.txt","r",stdin);
while(cin>>a[++n]);
sort(a+1,a+1+n);
int t=a[1].length();//先累加第一个单词的长度
for(int i=2;i<=n;i++){//依次计算每个单词对前一个单词的差
int j=0;
while(a[i][j]==a[i-1][j]&&j<a[i-1].length()) j++;//求俩个单词相等部分的长度
t+=a[i].length()-j;//累加俩个单词差 a[i].length()-j;
}
cout<<t+1<<endl;
return 0;
}
1338:【例3-3】医院设置
【题目描述】
设有一棵二叉树(如下图),其中圈中的数字表示结点中居民的人口,圈边上数字表示结点编号。现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻结点之间的距离为11。就本图而言,若医院建在11处,则距离和=4+12+2×20+2×40=136=4+12+2×20+2×40=136;若医院建在33处,则距离和=4×2+13+20+40=81=4×2+13+20+40=81……
【输入】
第一行一个整数nn,表示树的结点数(n≤100n≤100)。接下来的nn行每行描述了一个结点的状况,包含三个整数,整数之间用空格(一个或多个)分隔,其中:第一个数为居民人口数;第二个数为左链接,为00表示无链接;第三个数为右链接,为00表示无链接。
【输出】
一个整数,表示最小距离和。
【输入样例】
5
13 2 3
4 0 0
12 4 5
20 0 0
40 0 0
【输出样例】
81
#include<bits/stdc++.h>
#include<cstdio>
using namespace std;
const int maxn=0x3f3f3f3f;
int a[101];
int g[101][101];
int main()
{
int n;
cin>>n;
int l,r;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
g[i][j]=maxn;
for(int i=1;i<=n;i++)
{
g[i][i]=0;
cin>>a[i]>>l>>r;
if(l>0) g[i][l]=g[l][i]=1;
if(r>0) g[i][r]=g[r][i]=1;
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
}
int minn=maxn;
int sum=0;
for(int i=1;i<=n;i++)
{
sum=0;
for(int j=1;j<=n;j++)
{
sum+=g[i][j]*a[j];
}
if(sum<minn)
minn=sum;
}
cout<<minn<<endl;
return 0;
}
1339:【例3-4】求后序遍历
【题目描述】
输入一棵二叉树的先序和中序遍历序列,输出其后序遍历序列。
【输入】
共两行,第一行一个字符串,表示树的先序遍历,第二行一个字符串,表示树的中序遍历。树的结点一律用小写字母表示。
【输出】
一行,表示树的后序遍历序列。
【输入样例】
abdec
dbeac
【输出样例】
debca
#include<bits/stdc++.h>
using namespace std;
string a;
string b;
void f(int l1,int r1,int l2,int r2)
{
int mid=b.find(a[l1]);
//如果根结点大于中序序列左子树的结点,那么中序序列肯定有左子树
if(mid>l2) f(l1+1,l1+(mid-l2),l2,mid-1);//(m-l2)代表的在中序序列中左子树的个数
//如果根节点小于中序序列的右子树的最后一个,那么它就有右子树
if(mid<r2) f(l1+(mid-l2)+1,r1,mid+1,r2);
//当遍历完输出叶结点,然后回溯输出不是叶结点的结点
cout<<a[l1];
}
int main()
{
ios::sync_with_stdio(false);
cin>>a>>b;
f(0,a.size()-1,0,b.size()-1);
return 0;
}
1340:【例3-5】扩展二叉树
【题目描述】
由于先序、中序和后序序列中的任一个都不能唯一确定一棵二叉树,所以对二叉树做如下处理,将二叉树的空结点用·补齐,如图所示。我们把这样处理后的二叉树称为原二叉树的扩展二叉树,扩展二叉树的先序和后序序列能唯一确定其二叉树。
现给出扩展二叉树的先序序列,要求输出其中序和后序序列。
【输入】
扩展二叉树的先序序列。
【输出】
输出其中序和后序序列。
【输入样例】
ABD…EF…G…C…
【输出样例】
DBFEGAC
DFGEBCA
#include<bits/stdc++.h>
using namespace std;
typedef struct node{
char date;
node* lchild;
node* rchild;
};
node *bt;
int i;
string s;
void build(node* &bt){
if(s[++i] != '.'){
bt=new node;
bt->date=s[i];
build(bt->lchild);
build(bt->rchild);
}
else bt =NULL;
}
void inord(node* bt){
if(bt){
inord(bt->lchild);
cout<<bt->date;
inord(bt->rchild);
}
}
void postord(node* bt){
if(bt){
postord(bt->lchild);
postord(bt->rchild);
cout<<bt->date;
}
}
int main()
{
i=-1;
cin>>s;
build(bt);
inord(bt);
cout<<endl;
postord(bt);
cout<<endl;
return 0;
}
1372:小明的账单
【题目描述】
小明在一次聚会中,不慎遗失了自己的钱包,在接下来的日子,面对小明的将是一系列的补卡手续和堆积的账单… 在小明的百般恳求下,老板最终同意延缓账单的支付时间。可老板又提出,必须从目前还没有支付的所有账单中选出面额最大和最小的两张,并把他们付清。还没有支付的账单会被保留到下一天。 请你帮他计算出支付的顺序。
【输入】
第1行:一个正整数N(N≤15,000),表示小明补办银联卡总共的天数。
第2行到第N+1 行:每一行描述一天中收到的帐单。先是一个非负整数M≤100,表示当天收到的账单数,后跟M个正整数(都小于1,000,000,000),表示每张帐单的面额。
输入数据保证每天都可以支付两张帐单。
【输出】
输出共N 行,每行两个用空格分隔的整数,分别表示当天支付的面额最小和最大的支票的面额。
【输入样例】
4
3 3 6 5
2 8 2
3 7 1 7
0
【输出样例】
3 6
2 8
1 7
5 7
#include <bits/stdc++.h>
using namespace std;
int read() //快读
{
int x;
char c;
for(c=getchar();c<'0'||c>'9';c=getchar());
for(x=0;c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
return x;
}
int bill[30105],end=0,cnt[105],ar[30105]; //前后15000加上一天100=30100,end末尾的,cnt,ar辅助
int main()
{
int n=read(),i,j,m;
for(i=1;i<=n;i++){
for(j=m=read();j>=1;j--)
cnt[j]=read();
sort(cnt+1,cnt+1+m); //排序
int bp=1,cp=1;j=1; //归并(没有归只有并)
while(bp<=end&&cp<=m)
if(bill[bp]<cnt[cp])
ar[j++]=bill[bp++];
else
ar[j++]=cnt[cp++];
while(bp<=end)
ar[j++]=bill[bp++];
while(cp<=m)
ar[j++]=cnt[cp++];
printf("%d %d\n",ar[1],ar[end+m]); //第一个和最后一个
int day=n-i; //因为今天的已经付了,所以前后n-i个有用
end+=m-2;
if(day*2>=end){ //个数少全部有用
for(j=1;j<=end;j++)
bill[j]=ar[j+1];
}else{
int cont=1;
for(j=1;j<=day;j++)
bill[cont++]=ar[j+1];
for(j=end-day+2;j<=end+1;j++) //中间删去
ill[cont++]=ar[j];
end=cont-1;
}
}
return 0;
}
1371:看病
【题目描述】
有个朋友在医院工作,想请BSNY帮忙做个登记系统。具体是这样的,最近来医院看病的人越来越多了,因此很多人要排队,只有当空闲时放一批病人看病。但医院的排队不同其他排队,因为多数情况下,需要病情严重的人优先看病,所以希望BSNY设计系统时,以病情的严重情况作为优先级,判断接下来谁可以去看病。
【输入】
第一行输入n,表示有n个操作。
对于每个操作,首先输入push或pop。
push的情况,之后会输入ai 和 bi,分别表示患者姓名和患者病情优先级。
pop后面没有输入,但需要你输出。
【输出】
对于pop的操作,输出此时还在排队人中,优先级最大的患者姓名和优先级。
表示他可以进去看病了。
如果此时没人在排队,那么输出”none”,具体可见样例。
【输入样例】
7
pop
push bob 3
push tom 5
push ella 1
pop
push zkw 4
pop
【输出样例】
none
tom 5
zkw 4
【提示】【数据规模和约定】
1≤n≤100000,每个人的优先级都不一样,0≤优先级≤2000000000。
姓名都是小写字母组成的,长度小于20。
#include<bits/stdc++.h>
using namespace std;
struct per
{
int num,f;
char id[22];
} s;
bool operator < (const per&x,const per&y)
{
if(x.f==y.f)
return x.num <y.num;
else
return x.f>y.f;
}
int main()
{
int n,a,b;
scanf("%d\n",&n);
char str[10],name[21];
priority_queue<per>q;
int k=1;
for(int j=1; j<=n; j++)
{
scanf("%s",str);
if(strcmp(str,"push")==0)
{
scanf("%s%d",name,&a);
s.f=k;
s.num=a;
for(int i=0; i<strlen(name); i++)
s.id[i]=name[i];
q.push(s);
}
else
{
if(!q.empty())
{
s=q.top();
q.pop();
printf("%s %d\n",s.id,s.num);
}
else
printf("none\n");
}
}
}