20200411树和堆的应用

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");
        }
    }
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值