数据结构第一次实验报告

目录

 JLU数据结构荣誉课——第一次实验

7-1 重复计数(100分)

输入格式:

输出格式:

输入样例:

7-2 报数游戏 (100 分)

输入格式:

输出格式:

输入样例:

输出样例:

三.7-3 算术表达式计算 (100 分)

输入格式:

输出格式:

输入样例:

输出样例:

四.7-4 最喜爱的序列 (100 分)

输入格式:

输出格式:

输入样例:

输出样例:


 JLU数据结构荣誉课——第一次实验

7-1 重复计数(100分)

在一个有限的正整数序列中,有些数会多次重复出现。请你统计每个数的出现次数,然后按数字在序列中第一次出现的位置顺序输出数及其次数。

输入格式:

第1行,1个整数N,表示整数的个数,(1≤N≤50000)。

第2行,N个正整数,每个整数x 都满足 1 ≤ x ≤2000000000。

输出格式:

12
8 2 8 2 2 11 1 1 8 1 13 13

若干行,每行两个用一个空格隔开的数,第一个是数列中出现的数,第二个是该数在序列中出现的次数。

输入样例:

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

8 3
2 3
11 1
1 3
13 2

思路一、

题目限制数的大小范围是 1 ≤ x ≤2000000000,最暴力的方法就是开一个2000000001的数组,统计每个数字出现的次数。考虑输出的顺序,需要进行第二次遍历并且另加一个数组,来判断数据之前是否输出。由于空间要求过大,我并没有进行尝试。

思路二、使用STL中的map。

看了dalao们的题解,我发现这道题还可以用map+set+queue来做。我是用了一个map,并使用了一个存储数据和次数的结构体来做的。

结构体:

struct fenlei
{
    int sum; //次数
    int num; //数字
}lei[50001];

map中key存储数字,value中存储kind(初始为1,之后每出现一个之前没出现的数,kind++),若一个数之前没出现过,就取当前以kind为下标的结构体,这个数存到sum中,num++,value域存kind值;若一个数之前出现过,只需把相应的数字出现次数加1.

#include <iostream>
#include<map>
using namespace std;
map<int ,int>mp;
struct fenlei
{
    int sum; //次数
    int num; //数字
}lei[50001];
int kind=1;
int main()
{
    int n;cin>>n;
    int x;
    for(int i=1;i<=n;i++)
    {
        cin>>x;
        if(!mp[x])  
        {
            mp[x]=kind;
            lei[kind].sum++;
            lei[kind].num=x;
            kind++;
        }
        else
        {
            lei[mp[x]].sum++;
        }
    }
    for(int i=1;i<kind;i++)
    {
        cout<<lei[i].num<<" "<<lei[i].sum<<endl;
    }
    return 0;


}

思路三、谷老师说出这道题的本意是让我们用排序做,加深对sort函数的理解。本来我没有想到,因为按我当时认为排好序后,很难按原来的顺序输出(我太菜了)。后来用一个queue存储次序,lower_bound,upper_bound求数字出现的次数。

lower_bound与upper_bound函数都有三个参数,第一个参数p1为开始查找位置,第二个参数p2为第一个查找失效位置,第三个参数为要查找的数,这个函数查找一次的时间复杂度为O(log(p2-p1)),时间效率很好

我之后又写了一个代码(借鉴了一下dalao们的代码)

#include <iostream>
#include<algorithm>
#include <queue>
#include <set>
using namespace std;
int i,j,a[50001];
queue<int> q;
set<int> st;
int main()
{
	int n;
	cin>>n;
	for(i=1;i<=n;i++){
		cin>>a[i];
		if(!st.count(a[i]))
		{
			q.push(a[i]);
			st.insert(a[i]);
		}
	}
	sort(a+1,a+n+1);
	while(!q.empty())
	{
		int x=q.front();
		printf("%d %d\n",x,upper_bound(a+1,a+n+1,x)-lower_bound(a+1,a+1+n,x));
		q.pop();
	} 
}

7-2 报数游戏 (100 分)

n个人围成一圈,从1开始依次编号,做报数游戏。 现指定从第1个人开始报数,报数到第m个人时,该人出圈,然后从其下一个人重新开始报数,仍是报数到第m个人出圈,如此重复下去,直到所有人都出圈。总人数不足m时将循环报数。请输出所有人出圈的顺序。

输入格式:

一行,两个整数n和m。n表示游戏的人数,m表示报数出圈的数字,1≤n≤50000,1≤m≤100.

输出格式:

一行,n个用空格分隔的整数,表示所有人出圈的顺序

输入样例:

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

5 2

输出样例:

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

2 4 1 5 3

这是一道经典的约瑟夫问题。上课时老师讲了一种既实现简单,有快速的方法——静态双向链表,通过“跳舞”的方法来实现删除操作。

#include <iostream>
using namespace std;
int l[50001],r[50001];
int main()
{
    int n,k;
    cin>>n>>k;
    int i,m=0;
    for(i=1;i<=n;i++)
    {
        l[i]=i-1;
        r[i]=i+1;
    }
    l[1]=n;
    r[n]=1;
    r[0]=1;
    for(int j=1;j<=n;j++)
    {
        for(int x=0;x<k;x++)
        {
            m=r[m];
        }
        cout<<m;
        if(j!=n) cout<<" ";
        r[l[m]]=r[m];
        l[r[m]]=l[m];
    }
    return 0;
}

三.7-3 算术表达式计算 (100 分)

任务: 计算算术表达式的值。

算术表达式按中缀给出,以=号结束,包括+,-,,/四种运算和(、)分隔符。运算数的范围是非负整数,没有正负符号,小于等于109 。

计算过程中,如果出现除数为0的情况,表达式的结果为”NaN” ; 如果中间结果超出32位有符号整型范围,仍按整型计算,不必特殊处理。 输入保证表达式正确。

输入格式:

一行,包括1个算术表达式。算术表达式的长度小于等于1000。

输出格式:

一行,算术表达式的值 。

输入样例:

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

(1+30)/3=

输出样例:

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

10

这道题目难度不是很大,难点是代码量过大,容易犯小错误

基本思路:每次读入一个字符。如果是数字,进数字栈;如果是字符:1.栈空进栈     2.当前字符优先级大于栈顶元素优先级或当前字符为‘ ( ’,进栈     3.当前字符为右括号,弾栈计算到左括号      4.其余情况为当前元素优先级小于栈顶元素,此时一直弾栈直到栈顶元素优先级小于等于当前元素,此时当前元素进栈。   5.字符为‘=’ 结束,弹字符栈直到空,数字栈顶元素即为结果。

一些小问题的实现:

1.优先级的比较:第一时间想到用ACILL码进行比较,但是行不通,因为* /优先级相同,但是ACILL码不同,所以我开了一个int gai[128],并将下标为+ -  * / 的值设为优先级(+,-设为1,* / 设为2),比较时直接进行数组比较

2.数字的读入:防止出现多位数字,当读入的字符是数字时,会继续读入下一个字符,一直读到不是数字的字符,这时计算得出数字大小并入栈。

#include <iostream>
#include<stdlib.h>
#include<string.h>
using namespace std;
#define N 1
int  sta1[10000],top1,top2;
int gai[128];
char sta2[10000];
char alg[20000];
int p=0;
inline bool num(char x)
{
    if(x>='0'&&x<='9')  return true;
    return false;
}
int suan(char c,int a,int b)
{
    if(c=='+') return a+b;
    else if(c=='-') return b-a;
    else if(c=='*') return b*a;
    else if(c=='/'&&a!=0) return b/a;
    if(a==0&&c=='/') return INT_MAX;
}
int main()
{
    top1=-1;top2=-1;p=0;
    int flag;
	gai['+']=N; gai['-']=N; gai['*']=N+1; gai['/']=N+1; gai['(']=N+2;
	scanf("%s",alg);
	int len=strlen(alg);
	char c=alg[p++];
    while(c!='=')
    {
        int n=0;flag=0;
        while(num(c)){
        	flag=1;
			n=n*10+(c-'0');
			c=alg[p++];
		}
        if(flag)sta1[++top1]=n;
        while(!num(c)&&c!='=')
        {
            if(top2==-1) sta2[++top2]=c;
            else if(gai[c]<N+2&&gai[c]>gai[sta2[top2]]){
                sta2[++top2]=c;
            }
            else if(sta2[top2]=='('||c=='('){
            	sta2[++top2]=c;
			}
            else if(c==')'){
                while(sta2[top2]!='('){
                    int x1,x2,x4;
                    x1=sta1[top1--];
                    x2=sta1[top1--];
                    char x3;
                    x3=sta2[top2--];
                    x4=suan(x3,x1,x2);
                    sta1[++top1]=x4;
                }
                top2--;
            }
            else {
                while(top2!=-1&&gai[c]<=gai[sta2[top2]]&&sta2[top2]!='('){
                    int x1,x2,x4;
                    x1=sta1[top1--];
                    x2=sta1[top1--];
                    char x3;
                    x3=sta2[top2--];
                    x4=suan(x3,x1,x2);
                    sta1[++top1]=x4;
                    if(sta1[top1]==INT_MAX)
                    {
                    	printf("NaN");
                    	exit(0);
					}
                }
                sta2[++top2]=c;
            }

            c=alg[p++];
        }
    }
    while(top2!=-1){
        int x1,x2,x4;
        x1=sta1[top1--];
        x2=sta1[top1--];
        char x3;
        x3=sta2[top2--];
        x4=suan(x3,x1,x2);
        sta1[++top1]=x4;
        if(sta1[top1]==INT_MAX)
        {
        	printf("NaN");
        	exit(0);
		}
    }
    cout<<sta1[0];
    return 0;
}

四.7-4 最喜爱的序列 (100 分)

 小唐这段时间在研究序列。拿来N个整数的序列,他给序列中的每个整数都赋予一个喜爱值。喜爱值也是整数,有正有负,越大表明越喜欢。他想知道,如何从序列中连续取最多m个数,他获得喜爱值最大。1≤N≤500000,1≤m≤N。

输入格式:

第一行是两个整数N,m。分别代表序列中数的个数以及能取的最多个数。

第二行用空格隔开的N个整数,第i个整数Li代表他对第i个数的喜爱值。│Li│≤1000

输出格式:

一行,三个数,表示获得最大喜爱值,及第一个取最大喜爱值的区间。

输入样例:

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

5 2
1 4 5 2 3

输出样例:

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

9 2 3

考试时我的错误解法:

题目数取最多m个数,我看成了取m个数结果就直接写了一个贪心算法,每次去相邻的m个,如果比最大的大,就更新最大值,记录左右区间。

#include <iostream>
using namespace std;
int a[500001];
int main()
{
    int n,m,sum=0,sum1;
    cin>>n>>m;
    int b[2];
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=m;i++){sum+=a[i];}
    b[0]=1;b[1]=m;sum1=sum;
    for(int i=m+1;i<=n;i++)
    {
        sum1+=a[i];
        sum1-=a[i-m];
        if(sum1>sum)
        {
            b[0]=i-m+1;
            b[1]=i;
            sum=sum1;
        }
    }
    cout<<sum<<" "<<b[0]<<" "<<b[1];
    return 0;
}

于是就骗到了80分......老师写测试样例还是体谅我们第一次上机(嘿嘿).

 

后来老师说这道题应该用课上讲的单调队列,保持一个单调递增的序列,如果当前数小于队尾,就弹出队尾直到不小于队尾,之后当前元素进队尾,如果加入后队不满足区间要求,就弹出队头。我使用数组模拟进队出队操作。

首先数组改存前i个数的和,这样两数相减就得到了区间和(实验证明int数组是够用的)。

关于算法正确性:先假设sum[j]-sum[i]为最大值(j-i<=m)。在依次进队时,i一定可以进队,之后的x∈[i+1,j)范围内,一定不存在sum[j]-sum[x]>sum[j]-sum[i],否则与假设矛盾,而j进队后,i一定在队首,否则也与假设矛盾,所以j进队后,一定取到了这个最大值并记录下来,所以这个算法是正确的

#include <iostream>
using namespace std;
int sum[500001];
int que[500001];
int f=1,r=1;
int x,y,maxx;
int main()
{
    int n,m,i;cin>>n>>m;
    for(i=1;i<=n;i++)
    {
        cin>>sum[i];
        sum[i]+=sum[i-1];
    }
    for(i=1;i<=n;i++)
    {
        while(f<r&&sum[i]<sum[que[r-1]]) r--;
        que[r++]=i;
        while(f<r&&que[f]<i-m) f++;
        if(maxx<sum[que[r-1]]-sum[que[f]])
        {
            x=que[f]+1;
            y=que[r-1];
            maxx=sum[que[r-1]]-sum[que[f]];
        }
    }
    printf("%d %d %d",maxx,x,y);
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值