数据结构第一次解题报告

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

一.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

解法一:
根据题意可以直接开两个2000000000的数组,将数字的值做下标,一个记录数字的个数,一个记录数字是否被访问,将所有数字保存在一个数组中,遍历一次这个数组,并将已数字为下标的数组元素加一,并把标记数组置1,
再遍历一次数组,若该数字的标记数组为1则输出该数字及该数字的个数,并把该数字的标记数组置0。这样就会保持数据原有的顺序并且不会重复输出。这样就不需要多次遍历数组操作都是时间复杂度都是线性的。
缺点就是要开很大的数组空间可能会被题目的空间限制卡住,自己的编译器好像是不给开这么大的空间的。
具体代码实现如下:

#include<stdio.h>
int x[20000000]={0};//记录是否被访问 
int b[20000000]={0};//记录个数 
long long a[50000];
int main()
{
	int n;
	long long temp;
	int num=0;
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%lld",&temp);
		x[temp]=1;
		a[i]=temp;
		b[temp]++;
	}
	for(int i=0;i<n;i++)
	{
		if(x[a[i]]==1)
		{
			printf("%d %lld\n",a[i],b[a[i]]);
			x[a[i]]=0;
		}
	}
 } 

**解法二:**暴力法
直接读入数据,用一个数组存数字,一个数组存数字个数,对应下标相同,读入一个数字遍历之前读入的数据,如相同就将数字个数加1,若不相同再放入新的数组中。
具体代码实现:

#include<stdio>

int main()
{
    int N;
    scanf("%d",&N);
    int order[N];
    int freq[N];
    int num;
    int count_=0;
    for(int i=0;i<N;i++){
        order[i]=0;
        freq[i]=1;
    }
    for(int i=0;i<N;i++){
        int flag;
        flag=1;
        scanf("%d",&num);
        for(int j=0;j<count_;j++){
            if(order[j]==num){
                freq[j]++;
                flag=0;
            }
        }
        if(flag){
            count_++;
            order[count_-1]=num;
        }
    }
    for(int i=0;i<count_;i++){
        if(i!=0)
            printf("\n");
        printf("%d %d",order[i],freq[i]);
    }
    return 0;
}

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
解答:
这是经典的约瑟夫环问题,上学期的程序设计课程上机考过,当时用的是暴力法,因为数据量比较小,所以可以通过,这里数据量比较大,需要我们对算法做一些优化。
**解法一:**使用数组的方法,之前用的方法是将数组的元素删除再继续遍历,但删除操作涉及多个数组元素的移动非常的费时间,我们可以用一个标记数组来标记元素是否输出,遍历时输出下标并把标记数组置0即可。
具体代码实现:

#include<stdio>

int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    int a[n+1];
    for(int i=1;i<n+1;i++)
        a[i]=1;
    int count_=1;
    int left=n;
    int cur=1;
    while(left>0&&m!=1){
        if(cur==n){
            cur=1;
        }else{
            cur++;
        }
        if(a[cur]==1)
            count_++;
        if(count_==m){
            count_=0;
            left--;
            printf("%d",cur);
            a[cur]=0;
            if(left!=0)
                printf(" ");
        }
    }
    if(m==1){
        for(int i=1;i<n+1;i++){
            if(i!=1)
                printf(" ");
            printf("%d",i);
        }
    }
    return 0;
}

**解法二:**可以使用循环链表删除结点和遍历都非常方便。
删除操作是O(1)的,遍历是O(n*m)的。
具体代码实现如下:

#include<stdio.h>
#include<malloc.h>
struct a
{
	int n;
	struct a*next;
};
int main()
{
    int n,m,k=1;
    int i=1;
    scanf("%d%d",&n,&m);
    if(m==1)
    {
    	for(i=1;i<=n-1;i++)
		printf("%d ",i);
		printf("%d",i); 
	}
	else
	{
	int x[n];
    struct a*head=0,*p0=0,*p1=0,*p2=0;
	p0=(struct a*)malloc(sizeof(struct a));
	p0->next=p0;
	p0->n=i;
	p0->next=0;
	p1=head=p0;
	for(i=2;i<=n;i++)
	{
		p0=(struct a*)malloc(sizeof(struct a));
		p0->n=i;
		p1->next=p0;
		p1=p0;
	}
	p1->next=head;
	p1=head;
	for(i=0;i<n;i++)
	{
		while(k!=m)
		{
			p2=p1;
			p1=p1->next;
			k++;
		}
		k=1;
		x[i]=p1->n;
		p2->next=p1->next;
		p1=p1->next;
	}
	for(i=0;i<n-1;i++)
	printf("%d ",x[i]);
	printf("%d",x[n-1]);	
	}
 } 

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

任务: 计算算术表达式的值。
算术表达式按中缀给出,以=号结束,包括+,-,/四种运算和(、)分隔符。运算数的范围是非负整数,没有正负符号,小于等于109 。
计算过程中,如果出现除数为0的情况,表达式的结果为”NaN” ; 如果中间结果超出32位有符号整型范围,仍按整型计算,不必特殊处理。 输入保证表达式正确。
在这里插入图片描述
输入格式:
一行,包括1个算术表达式。算术表达式的长度小于等于1000。

输出格式:
一行,算术表达式的值 。

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

(1+30)/3=
输出样例:
在这里给出相应的输出。例如:

10
**解答:**题意是计算中缀表达式的值,可将中缀表达式转化为后缀表达式再计算,这里采用边转化边计算的方法减少扫描次数。
我是先把表达式当作字符串读入再一位位处理(不处理最后一位的等号),用一个栈stackq保存操作数,一个栈stacks保存操作符,考虑到可能有多位数的情况,再读入操作数时设置了一个flag来标记上一个读入的是否是操作数,如果是就stackq栈顶元素取出乘10再加上当前读入的操作数。

if(a[i]>='0'&&a[i]<='9')
    	{
    		if(flag==0)
    		{
    			topq++;
    			stackq[topq]=a[i]-'0';
    			flag=1;
			}
			else
			{
			stackq[topq]=stackq[topq]*10+a[i]-'0';
			}
		}

如果读入的是操作符如果栈空就直接入栈,栈不空时,如果栈顶是左括号或栈顶操作符优先级小于当前读入的操作符则该操作符直接入栈,如果该操作符是右括号,将stackq中弹出两个操作数stacks中弹出一个操作符,计算后再压入stackq直到栈顶是左括号,并把左括号弹出,如果前两种情况都不满足,则一直弹栈计算直到栈顶操作符的优先级大于等于该操作符或栈空。
对于优先级的判断和两个操作数的计算可以定义两个函数实现,避免写大量switch和if。

 int cal(int d1,int d2,char t)
 {
 	if(t=='+')
 	return d1+d2;
 	if(t=='-')
 	return d1-d2;
 	if(t=='*')
 	return d1*d2;
 	if(t=='/')
 	return d1/d2;
 }
int pro(char a)
{
	if(a=='(')
	return 3;
	else if(a=='+'||a=='-')
	return 1;
	else if(a==')')
	return 0;
	else return 2;
}

最后把stacks栈弹空并计算,stackq栈顶元素即为所得。
具体代码实现:


#include<stdio.h>
#include<string.h>
int cal(int d1,int d2,char t);
int pro(char a)
{
	if(a=='(')
	return 3;
	else if(a=='+'||a=='-')
	return 1;
	else if(a==')')
	return 0;
	else return 2;
}
int main()
{
	int d1,d2;
	char t;
	char a[10000];
	int stackq[10000];
	char stacks[100000];
	int topq=-1,tops=-1;
	int flag=0,flag2=0;
    scanf("%s",a);
    int n=strlen(a),i;
    for(i=0;i<n-1;i++)
    {
    	if(a[i]>='0'&&a[i]<='9')
    	{
    		if(flag==0)
    		{
    			topq++;
    			stackq[topq]=a[i]-'0';
    			flag=1;
			}
			else
			{
			stackq[topq]=stackq[topq]*10+a[i]-'0';
			}
		}
		else
		{
		
		   if(tops==-1)
			{
				tops++;
				stacks[tops]=a[i];
			}
			else
			{
			   if(a[i]==')')
				{
					while(stacks[tops]!='(')
					{
						d2=stackq[topq];
						topq--;
						d1=stackq[topq];
						t=stacks[tops];
						tops--;
						if(d2==0&&t=='/')
						{
							flag2=1; 
							printf("NaN");
		                   return 0;
						}
						stackq[topq]=cal(d1,d2,t);
						
					}
					tops--;
				}
				else if(pro(a[i])>pro(stacks[tops])||stacks[tops]=='(')
				{
					tops++;
					stacks[tops]=a[i];
				}
				else
				{
                while(tops!=-1&&pro(stacks[tops])>=pro(a[i])&&stacks[tops]!='(')
					{
						d2=stackq[topq];
						topq--;
						d1=stackq[topq];
						t=stacks[tops];
						tops--;	
						if(d2==0&&t=='/')
						{
							flag2=1; 
							printf("NaN");
		                   return 0;
						}
						stackq[topq]=cal(d1,d2,t);
					
					}
					tops++;	
					stacks[tops]=a[i];
				}
			}
			 flag=0;
		}
	}
	while(tops!=-1)
	{
	d2=stackq[topq];
	topq--;
	d1=stackq[topq];
	t=stacks[tops];
	tops--;
	
	if(d2==0&&t=='/')
{
	flag2=1;
	printf("NaN");
	return 0;
}stackq[topq]=cal(d1,d2,t);
	}
	if(flag2==1)
	{
		printf("NaN");
		return 0;
	}
	else
	{
	printf("%d",stackq[topq]);	
	}
	 } 
 int cal(int d1,int d2,char t)
 {
 	if(t=='+')
 	return d1+d2;
 	if(t=='-')
 	return d1-d2;
 	if(t=='*')
 	return d1*d2;
 	if(t=='/')
 	return d1/d2;
 }

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个数,还误打误撞的得了80分。
错误代码如下:

#include<stdio.h>
int main()
{
	int n,m,k;
	scanf("%d%d",&n,&m);
	int num=0;
	int a[n],i;
	int flag=0; 
	int b[n-m];
	for(i=0;i<n;i++)
	{
		scanf("%d",&a[i]);
	}
	for(i=0;i<m;i++)
	{
		num+=a[i];
	}
	b[0]=num;
	for(i=1;i<=n-m;i++)
	{
		b[i]=b[i-1]-a[i-1]+a[i+m-1];
	}
	num=b[0];
	for(i=0;i<=n-m;i++)
	{
		if(b[i]>num)
		{
			num=b[i];
			k=i;
		}
	}
	for(i=0;i<n;i++)
	{
		if(a[i]>num)
		{
			num=a[i];
			flag=1;
			k=i;
		}
	}
	if(flag==0)
	printf("%d %d %d",num,k+1,k+m);
	else
	printf("%d %d %d",num,k+1,k+1);

这样显然是不行的。
按照题意应采用单调队列。
**解答:**首先将数据读入建立一个前缀和数组,再维护一个单调队列保证队头为m区间中的最小值,再用m区间中的前缀和减去这个最小值就能得到m区间中的最大值。
维护单调队列遇到队尾比该数大就出队,每次把前缀和与队头做差比较。
具体代码实现:

#include<stdio.h>
int main()
{
	int n,m,k;
	scanf("%d%d",&n,&m);
	int max=0;
	int a[n+1],i;
	int front=0,rear=0; 
	int que[n]={0};//建立一个队列 
	a[0]=0;
	int left=0,right=0;
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		a[i]+=a[i-1];//创建一个保存前缀和的数组 
	}
	for(i=1;i<=n;i++)
	{
		while(front!=rear&&a[que[rear-1]]>a[i])//维护队列的单调性 
		{
			rear--;
		}
		que[rear]=i;
		rear++;
		while(front!=rear&&i-que[front]>m)
		{
			front++;
		}
		if(max<a[i]-a[que[front]])
		{
			max=a[i]-a[que[front]];
			left=que[front]+1;
			right=i;
		}
	}
	printf("%d %d %d",max,left,right);
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值