数据结构第三次实验报告

数据结构第三次实验报告

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

思路:

这道题主要分为三部

  • 建树
  • 求最长路径数值
  • 具体路径

    这三部都可以递归实现,但如果全用递归,我们发现第二三部是会重复的求每个节点的高度,所以我们应该把二三部合并,即在求长度的时候顺便记录下路径。

我开始的时候是想着在结构体里面加上一个量用来记录节点的深度,类似这样:

typedef struct node
{
    int val;
    int deep;
    node*left;
    node*right;
}*Btree;

之后再通过比较左右子树的高度来确定往哪里走。这样就还要一个函数确定走向,倒不如直接在节点里记录向哪里走。

typedef struct node
{
    int val;
    bool way;  //左0,右1
    node*left;
    node*right;
}*Btree;

参考代码:

#include <iostream>
#include<queue>
using namespace std;
int i,j,n;
typedef struct node
{
    int val;
    bool way; 
    node*left;
    node*right;
}*Btree;
void bu(Btree& p)
{

    int x;scanf("%d",&x);
    if(x==-1) p=NULL;
    else
    {
        p=new node;
        p->val=x;
        bu(p->left);
        bu(p->right);
    }
}
int high(Btree p)
{

	if(p==0) return -1;
	int len1=high(p->left),len2=high(p->right);
    if(len1<=len2)
    {
    	p->way=1;
		return len2+1;
	}
    else
    {
    	p->way=0;
		return len1+1;
	}
}
int main()
{
    scanf("%d",&n);
    Btree pp;
    bu(pp);  
    int sja=high(pp);
    printf("%d\n",sja);
	Btree p=pp;
	while(p->left||p->right){
        printf("%d ",p->val);
        if(p->way) p=p->right;
		else p=p->left;
    }
    printf("%d",p->val);
    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 <iostream>
#include<queue>
#include<vector>
using namespace std;
int i,j,n,now=1;
char shunxu[100001];
int degree[100001];
queue<int> q;
vector<int >vec[100001];
void build(int n)
{
    for(int i=0;i<degree[n];i++)
    {
        vec[n].push_back(++now);
        build(now);
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(j=1;j<=n;j++){
        cin>>shunxu[j];
    }
    for(j=1;j<=n;j++){
        cin>>degree[j];
    }
    while(now<=n)
	{
		vec[0].push_back(now); 
		build(now);
		now++;
	}
    for(int i=0;i<vec[0].size();i++)
    q.push(vec[0][i]);
    q.pop();
    cout<<shunxu[1];
    for(int i=0;i<vec[1].size();i++)
    q.push(vec[1][i]);
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        if(x!=0) cout<<" "<<shunxu[x];
        for(int i=0;i<vec[x].size();i++)
        {
            q.push(vec[x][i]);
        }

    }
    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

思路:

这道题开始时没有什么思路,但通过观察测试样例发现如果倒着看输出,就是每次取两个最小,这不就是反向哈夫曼吗(看成原本有n个长度随机的纸带,每次选两个最小的进行拼接,直到拼成一整条纸带),所以这道题的思路就解决了。

解决:

这道题时间限制为100ms,最多有100000个纸带,所以O(n2)的算法是不可取的,所以我们应该采用堆实现取最小的操作(我比较懒,就使用了优先队列)

#include <iostream>
#include<queue>
#include<algorithm>
#include<stdlib.h>
using namespace std;
typedef long long ll;
priority_queue <int,vector<int>,greater<int> >pq;
ll a[100001],top,sum;
int main()
{
    ios::sync_with_stdio(false);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        int x;cin>>x;
        pq.push(x);
    }
    if(n==1)
    {
        cout<<0<<endl;
        exit(0);
    }
    for(int i=1;i<n;i++)
    {
        int x1=pq.top();
        pq.pop();
        int x2=pq.top();
        pq.pop();
        sum+=(x1+x2);
        a[top++]=x1+x2;
        pq.push(x1+x2);
    }
    cout<<sum<<endl;
    cout<<a[n-2];
    for(int i=n-3;i>=0;i--) cout<<" "<<a[i];
}

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

思路:

给出的数组都是递增的,所以就没必要进行排序。这道题同样是100000个数据,100ms时间限制,所以我采用了O(nlogn)的算法解决。先算出第一个数组各个元素与第二个数组第一个元素的乘积存入优先队列,(存储的时候存入结构体,存最小值的同时存下两个乘数的下标,下次再存入时便于修改数值);

大数据读入时采用下面的方法会比较快:

inline int read()
{
	int x=0, w=0; char ch=0;
	while (!isdigit(ch)) w|=ch=='-', ch=getchar();
	while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48), ch=getchar();
	return w?-x:x;
}

 普通读入最后一组数据58ms,而采用上面的方法只有32ms,提速接近一半。

#include <iostream>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
int i,j;
int a[100001],b[100001];
int minn[100001],top[100001];
int endd[100001],tot=1;
struct node
{
    int x,y;
    int val;
}Node[100001];
bool operator<(node a,node b)
{
    return a.val>b.val;
}

priority_queue<node>pq;
inline int read()
{
	int x=0, w=0; char ch=0;
	while (!isdigit(ch)) w|=ch=='-', ch=getchar();
	while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48), ch=getchar();
	return w?-x:x;
}
int main()
{
    ios::sync_with_stdio(false);
    int n;cin>>n;
    for(i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    for(i=1;i<=n;i++)
    {
        cin>>b[i];
    }
    for(j=1;j<=n;j++){
        Node[j].x=j;
        Node[j].y=1;
        Node[j].val=a[j]*b[1];
        pq.push(Node[j]);
    }
    for(j=1;j<=n;j++)
    {
        node t=pq.top();
        if(j-n) cout<<t.val<<" ";
        else cout<<t.val;
        pq.pop();
        t.y++;
        t.val=a[t.x]*b[t.y];
        pq.push(t);
    }
    return 0;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值