2021-05-16

 

JLU第一次数据结构实验解题报告

目录

一、7-1 重复计数

二、7-2报数游戏

三、7-3 算数表达式计算

四、 7-4最喜爱的序列


 一、7-1 重复计数

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

输入格式:

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

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

输出格式:

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

输入样例:

12

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

输出样例:

8 3

2 3

11 1

1 3

13 2 

解题思路:

我用的是暴力解法O(n^2),就是先开一个符合题意的数组ent,把第一个数字存入数组第一个元素中,出现次数计为1,然后再读入新的数字,遍历数组ent,新读入的数字出现在ent中,就在ent数组中相应的数字所对应的次数加1,否则,将该数字加入ent数组,次数赋值1。但是它一开始只得了70分,错误是运行超时,我以为是暴力循环太多导致超时,后来听了同学的暴力解法 两个平台都通过了,我重新找了原因。改到最后发现把long long改成 int就通过了,然后突然觉得自己有一个重大发现。

以下是我的代码:

​#include<stdio.h>
struct tongji
{
	int name;
	int cishu;
};
struct tongji ent[50001];
int count=0;
int main()
{
	int N[50001];
	int i,j;
	int n;
	scanf("%d",&n);
	scanf("%d",&N[0]);
	ent[0].name=N[0];
	ent[0].cishu=1;
	for(i=1;i<n;i++)
	{
		scanf("%d",&N[i]);
		for(j=0;j<=count;j++)
		{
			if(ent[j].name==N[i])
			{
				ent[j].cishu++;
				break;
			}
		}
		if(j==count+1)
		{
            count++;
			ent[count].name=N[i];
			ent[count].cishu=1;
		}
   }
	for(i=0;i<=count;i++)
	{
		printf("%d %d",ent[i].name,ent[i].cishu);
		if(i!=count)printf("\n");
	}
	return 0;
}

​

 二、7-2报数游戏

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

输入格式:

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

输出格式:

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

  • 输入样例:

5 2 

输出样例:

2 4 1 5 3 

分析思路:这是约瑟夫问题,之前遇到过两次,我都是用数组做的,不过循环链表也可。数组是也是模拟的循环链表,他相比于链表来说的缺点是空间会被浪费掉。听同学的方法是给那个该出圈的人一个特别的标记就可以,下次访问到跳过就OK,这个方法也很好。 

这道题刚开始60分,原因也是我误用了 long long!!改成int 就好了。

以下是我的代码:

#include<stdio.h>
int main()
{
	int a[100];
	int i,m,n,k,j=0;
	scanf("%d %d",&n,&m);
	for(i=0;i<n;i++)
	{
		a[i]=i+1;
	}
	while(n>1)
	{
		j=(j+m-1)%n;      //被选中出圈人的序号 
		printf("%d ",a[j]);    //把他删除 
		for(k=j;k<n-1;k++)
		{
			a[k]=a[k+1];      //出圈人后面的前移,保证圈不断开 
		}
		n--;      // //把出圈的人删除 
	}
	printf("%d",a[0]);
	return 0;
}

​​​​​​​三、7-3 算数表达式计算

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

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

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

输入格式:

一串以=结束的合法算术表达式

输出格式:

算术表达式结果 

输入样例:

(30+1)/3= 

输出样例:

10

解题思路:

我的思路就是用的课上老师教的计算中缀表达式算法思想。辅助空间用两个栈,一个存数字,一个存运算符。我在实现代码的过程中犯了两个错误,一个是没有随时判断栈空导致段错误,一个是没有比较+、-、*、/自身与自身的优先级比较,导致出现了段错误与答案错误。具体实现请看代码。

​#include<bits/stdc++.h>
using namespace std;
stack <char> character;
stack <int> num;
bool Judge(char x){
	if(x>='0'&&x<='9')
	return true;
	return false;
}
char compare(char x,char y) //比较输入运算符和栈顶字符的优先级
{
	if(x=='+'){
		if(y=='*'||y=='/'||y=='-'||y=='+'){return '<';} //优先级相等的也弹出去参与计算
		else return '>';
	}
    if(x=='-'){
		if(y=='*'||y=='/'||y=='+'||y=='-'){return '<';}
	    else return '>';
	}
	if(x=='*'){
	   	if(y=='/'||y=='*')return '<';
	    else return '>';
	}
	if(x=='/'){
		if(y=='*'||y=='/')return '<';
		else return '>';
	}
	if(x=='('){
	   	return '>';
	}
}
void comput(char ent)
{
	int temp2=num.top();
	num.pop();
	int temp1=num.top();
	num.pop();
	switch(ent){
		case'+':{num.push(temp1+temp2);break;}
		case'-':{num.push(temp1-temp2);break;}
		case'*':{num.push(temp1*temp2);break;}
		case'/':{
		        	if(temp2==0){
			        	printf("NaN");
			        	exit(0);
		        	}
		        	else{
		        	//	printf("%d  ",temp2);
	                	num.push(temp1/temp2);
	                }
	                break;
		    	}
	}
//	printf("%d%c%d=%d  ",temp1,ent,temp2,num.top());
}
int main()
{
	char ch,com;
	int flag,j;
	ch=getchar();
	while(ch!='='){
		j=0;
		flag=0;
		while(Judge(ch)){
			j=j*10+ch-'0';
			ch=getchar();
			flag=1;
		}
		if(flag==1){num.push(j);}
		else{
		    	if(character.empty()) {
			    	character.push(ch);
		    	}
		    	else{
			    	if(ch==')'){
				    	while(character.top()!='('){
					    	comput(character.top());
					    	character.pop();
				    	}
					    character.pop();
			      	}
			       	else{
		    	        com=compare(ch,character.top());
			    	   	while((com=='<'&&!character.empty())){
			    		   	comput(character.top());
                            character.pop();  //有弹出就得判空,防止段错误,我在这儿出过错
                            if(character.empty()){break;}
                            com=compare(ch,character.top());
			    		}
			    		character.push(ch);
                    }
                  }
	             ch=getchar();
	      }
	}
	if(ch=='='){
		while(!character.empty()){
			comput(character.top());//有弹出就得判空
			character.pop();
		}
    }
	cout<<num.top();
	return 0;
} 

​

四、 7-4最喜爱的序列

小唐这段时间在研究序列。拿来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。当时看这个题目,觉得也不难嘛,不需要用双端队列。理解错题意竟然得了80分,后来问了一位大佬,才知道自己读错题目了。继续用数组就运行超时了。不得不向大佬学习巩固双端队列了。先求前缀和,存到一个数组,然后让数组元素下标进入队列,维护下标做对应元素递减次序并且队头队尾所表示区间长度不超过m。循环判断队头队尾元素之差与所保存的maxn的值的大小,如果大于,更新maxn,并保存对应区间。

以下是实现的代码:

#include<bits/stdc++.h>
using namespace std;
deque<int> q;
int main(void)
{
    int n, m, i, j;
    int front,rear;
    long long manx=1e-5;
    scanf("%d %d",&n,&m);
    long long line[500001];
    line[0]=0;
    for (i = 1; i <= n; i++)
    {
        scanf("%lld", &line[i]);
        line[i] += line[i-1];
    }
    long long maxn = 0;
    while (!q.empty())
    q.pop_back(); //弹出最后一个并把容器减小一个 
    q.push_front(0);  //把下标0压入 
    for (i=1; i<=n;i++)
    {
        while (!q.empty() && line[q.front()] > line[i]) //保持递减序列 
        {
           q.pop_front();
        }
        q.push_front(i);
        while (!q.empty() && i - q.back() > m)
        {
            q.pop_back();//如果区间的大了,就弹出dui头 
        }
        if(maxn< line[i] - line[q.back()])
        { 
            maxn = line[i] - line[q.back()];
            front = q.back()+1;
            rear = i;
        }
     }
    printf("%lld %d %d\n", maxn, front,rear);
    return 0;
}

交流解题报告真的是一种不错的体验,原来我在优快云上看到的写文章的大佬就在我身边 ,向你们看齐,加油!

 

 

 

 

 

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值