数据结构第三次上机实验

7-1 二叉树最长路径 (100 分)

给定一棵二叉树T,求T中的最长路径的长度,并输出此路径上各结点的值。若有多条最长路径,输出最右侧的那条。

输入格式:

第1行,1个整数n,表示二叉树有n个结点, 1≤n≤100000.

第2行,2n+1个整数,用空格分隔,表示T的扩展先根序列, -1表示空指针,结点用编号1到n表示。

输出格式:

第1行,1个整数length,length表示T中的最长路径的长度。

第2行,length+1个整数,用空格分隔,表示最右侧的最长路径。

输入样例:

在这里给出一组输入。例如:

5
1 2 -1 -1 3 4 -1 -1 5 -1 -1

输出样例:

在这里给出相应的输出。例如:

2
1 3 5

题目分析

即考察二叉树的建立与遍历。值得注意的是,题目要求输出最右最长路径。

代码实现如下

#include<stdio.h>
#include<malloc.h>
struct tree{
	int num;
	struct tree *lc,*rc;
};

struct tree *build(struct tree *p){
	int k;
	scanf("%d",&k);
	if(k==-1){
		return NULL;
	}
	else {
		p=(struct tree *)malloc(sizeof(struct tree));
		p->num=k;
		p->lc=build(p->lc);
		p->rc=build(p->rc);
		return p;
	}
}
struct tree* s[100010];
int tag[100010];
int lo[100010];
void finddeep(struct tree *root){
	struct tree *tem=root;
	int len=0,top=0;
	int i;
	do{
		while(tem){
		top++;
		s[top]=tem;
		tag[top]=0;
		tem=tem->rc;
		}
		if(top>0){
			if(tag[top]==1){
				if((s[top]->lc==NULL)&&(s[top]->rc==NULL)&&(top>len)){
					for(i=1;i<=top;i++){
						lo[i]=s[i]->num;
					}
					len=top;
				}
			top--;
			}
			else{
				tem=s[top];
				if(top>0){
					tem=tem->lc;
					tag[top]=1;
				}
			}
		}
	}while((tem!=NULL)||(top!=0));
	printf("%d\n",len-1);
	for(i=1;i<=len;i++){
		printf("%d",lo[i]);
        if(i!=len)printf(" ");
	}
    //printf("\n");
	return ;
}
int main(){
	int n,k,i=0;
	scanf("%d",&n);
	struct tree *root;
	root=build(root);
	finddeep(root);
	return 0;
}

7-2 森林的层次遍历 (100 分)

给定一个森林F,求F的层次遍历序列。森林由其先根序列及序列中每个结点的度给出。

输入格式:

第1行,1个整数n,表示森林的结点个数, 1≤n≤100000.

第2行,n个字符,用空格分隔,表示森林F的先根序列。字符为大小写字母及数字。

第3行,n个整数,用空格分隔,表示森林F的先根序列中每个结点对应的度。

输出格式:

1行,n个字符,用空格分隔,表示森林F的层次遍历序列。

输入样例:

在这里给出一组输入。例如:

14
A B C D E F G H I J K L M N
4 0 3 0 0 0 0 2 2 0 0 0 1 0

输出样例:

在这里给出相应的输出。例如:

A M B C G H N D E F I L J K

题目分析

即建立左儿子右兄弟树,使用队列,以进行层次遍历。当然,为了节省空间,并没有真正建树,只是保存了一个下标数组。

代码实现如下

#include<stdio.h>
#include<malloc.h>
int num[100000],lc[100000],rb[100000],s[100000];
char c[100000];
int visited[100000];
int queue[100000];
int main(){
	int n,k,i,p,top,tem;
	p=top=tem=0;
	scanf("%d\n",&n);
	for(i=0;i<n;i++){
		scanf("%c",&c[i]);
		getchar();
	}
	for(i=0;i<n;i++){
		scanf("%d",&num[i]);
		lc[i]=-1; 
		rb[i]=-1;
	}
	s[p]=0;
	while(top<n){
		if(num[tem]){
			num[tem]--;
			lc[tem]=++top;
			tem=top;
			s[++p]=tem;
		}
		else {
            if(p>0){
                if(num[s[--p]]){
				    rb[tem]=++top;
				    num[s[p]]--;
				    tem=top;
				    s[++p]=tem;
			    }
			    else {
				    tem=s[p];
			    }
		     }
            else{
                p--;
                if(top==n-1)break;
                rb[tem]=++top;
                tem=top;
                s[++p]=tem;
            }
        }
    }
	int f=0,r=0;
    queue[r++]=0;
    printf("%c",c[0]);
	while(f<r){
        int m=queue[f++];
        while(m!=-1){
            if(m!=0)
            printf(" %c",c[m]);
            if(lc[m]!=-1)queue[r++]=lc[m];
            m=rb[m];
       }
    }
    printf("");
	return 0;
}

7-3 纸带切割 (100 分)

有一条细长的纸带,长度为 L 个单位,宽度为一个单位。现在要将纸带切割成 n 段。每次切割把当前纸带分成两段,切割位置都在整数单位上,切割代价是当前切割纸带的总长度。每次切割都选择未达最终要求的最长纸带切割,若这样的纸带有多条,则任选一条切割。如何切割,才能完成任务,并且总代价最小。

输入格式:

第1行,1个整数n,表示切割成的段数, 1≤n≤100000.

第2行,n个整数Li,用空格分隔,表示要切割成的各段的长度,1≤Li≤200000000,1≤i≤n.

输出格式:

第1行,1个整数,表示最小的总代价。

第2行,若干个整数,用空格分隔,表示总代价最小时每次切割的代价。

输入样例:

在这里给出一组输入。例如:

5
5 6 7 2 4

输出样例:

在这里给出相应的输出。例如:

54
24 13 11 6

题目分析输出结果即为以输入项作为叶子节点,构建的哈夫曼树的圆节点的值。为了节省空间,并未真正构建哈夫曼树(因为会内存超限),而是每次直接取最小两权值和,先记录再进入优先队列中即可。

代码实现如下

#include<iostream>
#include<queue>
using namespace std;
int p[200010];
priority_queue <int,vector<int>,greater<int> > q;
int  w[100000];
int main(){
	int n,i,top=0;
	long long sum=0;
	int k;
	scanf("%d",&n);
	for(i=0;i<n;i++){
		scanf("%d",&k);
		q.push(k);
	}
	while(q.size()!=1){
		int x,y,z;
		x=q.top();
		q.pop();
		y=q.top();
		q.pop();
		z=x+y;
		sum+=z;
		q.push(z);
		w[top++]=z;
	}
	printf("%lld\n",sum);
	for(int i=top-1;i>=0;i--){
		printf("%d",w[i]);
		if(i!=0)printf(" ");
	}
	return 0;
}

7-4 序列乘积 (100 分)

两个递增序列A和B,长度都是n。令 Ai 和 Bj 做乘积,1≤i,j≤n.请输出n*n个乘积中从小到大的前n个。

输入格式:

第1行,1个整数n,表示序列的长度, 1≤n≤100000.

第2行,n个整数Ai,用空格分隔,表示序列A,1≤Ai≤40000,1≤i≤n.

第3行,n个整数Bi,用空格分隔,表示序列B,1≤Bi≤40000,1≤i≤n.

输出格式:

1行,n个整数,用空格分隔,表示序列乘积中的从小到大前n个。

输入样例:

在这里给出一组输入。例如:

5
1 3 5 7 9 
2 4 6 8 10

输出样例:

在这里给出相应的输出。例如:

2 4 6 6 8

题目分析

注意到两行输入都是递增序,则只需计算第一行乘以每一列的元素来保存到优先队列中,之后每弹出一个,则使该元素的下一行的元素与该元素本列的乘积入队,以保持队列的元素恒小于等于n,

否则会内存超限。

代码实现如下

#include<iostream>
#include<queue>
#include<utility>
using namespace std;
class aim{
public:
    int x,y,v;
    aim(int a,int b,int c):x(a),y(b),v(c){;}
};
struct cmp{
    bool operator()(aim &a,aim &b){return a.v>b.v;}
};
int a[100000];
int b[100000];
priority_queue<aim,vector<aim>,cmp> q;  
int main(){
    int n,k;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=0;i<n;i++){
        scanf("%d",&b[i]);
        if(i==0)for(int j=0;j<n;j++){
            q.push(aim(j,0,a[j]*b[0]));
            //printf("%d ",a[j]*k);
        }
    }
    for(int i=0;i<n-1;i++){
        aim r=q.top();
        printf("%d ",r.v);
        q.pop();
        if(r.y<n-1)q.push(aim(r.x,r.y+1,a[r.x]*b[r.y+1]));
    }
    printf("%d",q.top().v);
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值