JLU数据结构上机实验1

7-1 重复计数

原题呈现:
在这里插入图片描述
解题思路 :这道题数据可能比较水 这道题可以用暴力法求解。开一个二维数组a[50001][2],每一行的第一个数保存数列中的数,第二个数保存该数在数列中出现的次数。遍历数列,每读入一个数,遍历数组判断该数是否已经在数组中。如果是,出现次数+1;如果否,将该数保存在数组中并将出现次数初始化为1.

以下是根据这一思路写出的代码:

#include <stdio.h>
#include <malloc.h>
#include <string.h>

int main() {
	long long a[50001][2],temp;
	int n, i, j, count = 0,flag=0;
	scanf("%d", &n);
	for (i = 0; i < n; i++)
		a[i][1] = 0;
	for (i = 0; i < n; i++) {
		scanf("%lld", &temp);
		if (i == 0) {
			a[0][0] = temp;
			a[0][1] = 1;
			count = 1;
			continue;
		}
		for (j = 0; j < count; j++) {
			if (a[j][0] == temp) {
				a[j][1]++;
				flag = 1;
				break;
			}
		}
		if (!flag) {
			count++;
			a[count-1][0] = temp;
			a[count-1][1]++;
		}
		flag = 0;
	}
	for (i = 0; i < count; i++) {
		printf("%lld %lld\n", a[i][0], a[i][1]);
	}

	return 0;
}

解后反思:这道题数据真的比较水 以上方法时间复杂度是O(n^2)级别的,不尽如人意。可以考虑改进对一个数是否已经出现过的判断,一个可以采用的方法是利用STL中的set容器;另外,学习过炫神的博客后,我也发现了可以利用sort排序结合lower_bound,upper_bound很方便地求解这个问题。

7-2 报数游戏

原题呈现:
在这里插入图片描述

解题思路 : 这是一个典型的约瑟夫环问题,可以用循环链表解决。

以下是根据这一思路写出的代码:

#include <stdio.h>
#include <malloc.h>
#include <string.h>

typedef struct x {
	int num;
	struct x* next;
}xtype;
typedef xtype* pxtype;
pxtype head, rear, q, q0;

int main() {
	int n, m, i, count = 0;
	scanf("%d %d", &n, &m);

	for (i = 0; i < n; i++) {
		pxtype p;
		p = (pxtype)malloc(sizeof(xtype));
		p->num = i + 1;
		if (i == 0) {
			head = p;
			rear = p;
			p->next = head;
		}
		else {
			rear->next = p;
			rear = p;
			p->next = head;
		}
	}
	q0 = head;
	q = head;
	if (m == 1) {
		while (count < n) {
			printf("%d", q->num);
		    if (count != n - 1)
			    printf(" ");
			q = q->next;
			count++;
		}
		return 0;
	}
	while (count < n) {
		for (i = 1; i < m; i++) {
			q0 = q;
			q = q->next;
		}
		printf("%d", q->num);
		if (count != n - 1)
			printf(" ");
		q0->next = q->next;
		free(q);
		count++;
		q = q0->next;
	}

	return 0;
}

解后反思: 虽然循环链表能够解决这个问题,但是略显繁琐,可以用静态链表达到相同的效果。

7-3 算术表达式计算

原题呈现:
在这里插入图片描述

解题思路 : 开两个栈:一个数字栈保存数字,一个符号栈保存运算符。如果读入的是一个数字,直接入栈;如果读入的是运算符,如果栈空那么直接入栈,否则首先将其与符号栈顶的运算符优先级比较,如果栈顶的优先级大于等于当前运算符,那么需要先把栈顶的运算符计算掉。一直重复下去,直到栈空或者遇到栈顶的优先级比当前运算符的优先级低,这时当前运算符才能入栈。如果遇到了左括号,直接入栈;如果遇到了右括号,那么就把之前栈中的左括号和当前右括号中间夹着的运算符全部运算掉,同时把左括号出栈,右括号不用入栈。在表达式合法的前提下,最后数字栈的栈顶元素就是表达式的值。

以下是根据这一思路写出的代码:

#include <stdio.h>
#include <malloc.h>
#include <string.h>

int main(){
	char str[10000],ch;
	int len,s1[10000],s2[10000],top1=-1,top2=-1,i=0,temp=0;
	scanf("%s",str);
	len=strlen(str);
	while(1){
		if(str[i]=='='){
			while(top2!=-1){
				temp=0;
				if(s2[top2]=='+'){
					temp=s1[top1-1]+s1[top1];
					top1-=2;
					s1[++top1]=temp;
					top2--;
				}
				else if(s2[top2]=='-'){
					temp=s1[top1-1]-s1[top1];
					top1-=2;
					s1[++top1]=temp;
					top2--;
				}
				else if(s2[top2]=='*'){
					temp=s1[top1-1]*s1[top1];
					top1-=2;
					s1[++top1]=temp;
					top2--;
				}
				else if(s2[top2]=='/'){
					if(s1[top1]==0){
						printf("NaN");
						return 0;
					}	
					temp=s1[top1-1]/s1[top1];
					top1-=2;
					s1[++top1]=temp;
					top2--;
				}
			}
			printf("%d",s1[top1]);
			break;
		}
		else if(str[i]>='0'&&str[i]<='9'){
			temp=0;
			while(str[i]>='0'&&str[i]<='9'){
				temp=temp*10+str[i]-'0';
				i++;
			}
			s1[++top1]=temp;
		}
		else{
			if(str[i]==')'&&s2[top2]=='('){
				top2--;
			}
			else if(top2==-1||str[i]=='('||s2[top2]=='('||((str[i]=='*'||str[i]=='/')&&(s2[top2]=='+'||s2[top2]=='-'))){
				s2[++top2]=str[i];
			}
			else if(str[i]==')'){
				while(s2[top2]!='('){
					temp=0;
					if(s2[top2]=='+'){
						temp=s1[top1-1]+s1[top1];
						top1-=2;
						s1[++top1]=temp;
						top2--;
					}
					else if(s2[top2]=='-'){
						temp=s1[top1-1]-s1[top1];
						top1-=2;
						s1[++top1]=temp;
						top2--;
					}
					else if(s2[top2]=='*'){
						temp=s1[top1-1]*s1[top1];
						top1-=2;
						s1[++top1]=temp;
						top2--;
					}
					else if(s2[top2]=='/'){
						if(s1[top1]==0){
						printf("NaN");
						return 0;
					}	
						temp=s1[top1-1]/s1[top1];
						top1-=2;
						s1[++top1]=temp;
						top2--;
					}
				}
				top2--;
			}
			else{
				temp=0;
				if(s2[top2]=='+'){
					temp=s1[top1-1]+s1[top1];
				    top1-=2;
					s1[++top1]=temp;
					top2--;
				}
				else if(s2[top2]=='-'){
					temp=s1[top1-1]-s1[top1];
					top1-=2;
					s1[++top1]=temp;
					top2--;
				}
				else if(s2[top2]=='*'){
					temp=s1[top1-1]*s1[top1];
					top1-=2;
					s1[++top1]=temp;
					top2--;
				}
				else if(s2[top2]=='/'){
					if(s1[top1]==0){
						printf("NaN");
						return 0;
					}	    
					temp=s1[top1-1]/s1[top1];
					top1-=2;
					s1[++top1]=temp;
					top2--;
				}
				i--;
			}
			i++;
		}
	}
	
	return 0;
}

解后反思: 可以从两个方面简化这段冗杂的代码:1.将从栈顶取两个元素然后用当前运算符计算得出结果的过程用函数进行模块化封装,这样可以减少很多重复的代码;2.给不同的运算符赋不同的值来表示它们的优先级,这样可以简单地通过’>’ ‘=’ '<'来比较它们的优先级。

7-4 最喜爱的序列

原题呈现:
在这里插入图片描述

先上一版当时水到AC的代码:

#include<stdio.h>
#include<math.h>
#include<string.h>
#include<malloc.h>
#include<stdlib.h>

int a[500001],b[500001];

int main() {
	int  m, n, i,tmp=0,max,maxl,maxr;
	scanf("%d %d", &n, &m);
	int mm = m;
	while(mm>0){
	for (i = 0; i < mm; i++) {
		scanf("%d", &a[i]);
		tmp += a[i];
	}
	b[0] = tmp;
	if(mm==m){
	max = tmp;
	maxl = 1;
	maxr = mm;
	}
	for (i = mm; i < n; i++) {
		scanf("%d", &a[i]);
		tmp = b[i - mm] - a[i - mm] + a[i];
		b[i - mm + 1] = b[i - mm] - a[i - mm] + a[i];
		if (tmp > max) {
			max = tmp;
			maxl = i - mm + 2;
			maxr = i + 1;
		}
	}
	tmp = 0;
	mm--;
        if (mm > 10)
		break;
	}
    printf("%d %d %d", max, maxl, maxr);

	return 0;
}

这个方法能过完完全全是因为测试数据太水

下面给学习到的正确解法:

解题思路 : 前缀和+单调优先队列

以下是根据这一思路写出的代码:

#include<bits/stdc++.h>
using namespace std;
long long sum[500010];
deque<int> q;
int posl, posr;
int main()
{
    int n, m, i, j;
    scanf("%d %d", &n, &m);
    sum[0] = 0;
    for (i = 1; i <= n; i++)
    {
        scanf("%lld", &sum[i]);
        sum[i] += sum[i - 1];
    }//前缀和

    while (!q.empty())
        q.pop_back();
    //q.push_front(0);
    long long maxn = 0;
    for (i = 1; i <= n; i++)
    {
        while (!q.empty() && sum[q.front()] > sum[i])
        {
            q.pop_front();
        }
        q.push_front(i);//维护单调性
        while (!q.empty() && i - q.back() > m)
        {
            q.pop_back();
        }//维护区间长度不大于m
        int tmp = 0;
        if (q.back() == i) {
            tmp = sum[i] - sum[i - 1];
            if (maxn < tmp)
            {
                maxn = tmp;
                posl = i;
                posr = i;
            }
        }
        else {
            tmp = sum[i] - sum[q.back()];
            if (maxn < tmp)
            {
                maxn = tmp;
                posl = q.back() + 1;
                posr = i;
            }
        }
    }
    printf("%lld %d %d\n", maxn, posl, posr);
    return 0;
}

解后反思: 前缀和的思想其实不难想到,困难在于对单调队列的应用场景不熟悉。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
第一次实验: 题目1 单链表相关算法的实验验证。 [实验目的] 验证单链表及其上的基本操作。 [实验内容及要求] 1、 定义单链表类。 2、 实验验证如下算法的正确性、各种功能及指标: 1)创建单链表; 2)插入操作:分别在当前结点后、表头、表尾插入值为x的结点; 3)删除操作:分别删除表头结点、表尾结点和当前结点的后继结点; 4)存取操作:分别存取当前结点的值和单链表中第k个结点的值; 5)查找操作:查找值为x的元素在单链表中的位置(下标)。 题目2 分别给出堆栈、队列相关算法的实验验证。 [实验目的] 验证堆栈、队列及其上的基本操作。 [实验内容及要求](以队列为例) 1、 定义队列类。 2、 实验验证如下算法的正确性、各种功能及指标: 1)创建队列; 2)插入操作:向队尾插入值为x的元素; 3)删除操作:删除队首元素; 4)存取操作:读取队首元素。 第二次实验 题目1 二叉树相关算法的实验验证。 [实验目的] 验证二叉树的链接存储结构及其上的基本操作。 [实验内容及要求] 1、 定义链接存储的二叉树类。 2、 实验验证如下算法的正确性、各种功能及指标: 1)创建一棵二叉树,并对其初始化; 2)先根、中根、后根遍历二叉树(递归算法); 3)在二叉树中搜索给定结点的父结点; 4)搜索二叉树中符合数据域条件的结点; 5)从二叉树中删除给定结点及其左右子树。 题目2 树和森林的遍历算法的实验验证。 [实验目的] 验证树和森林的遍历算法。 [实验内容及要求] 1、 定义左儿子—右兄弟链接存储的树类和森林类。 2、 实验验证如下算法的正确性、各种功能及指标: 1)创建树和森林; 2)树和森林的先根遍历的递归和迭代算法; 3)树和森林的后根遍历的递归和迭代算法; 4)树和森林的层次遍历算法。 题目3 二叉查找树的验证实验。 [实验目的] 验证二叉查找树及其相关操作。 [实验内容及要求] 1、 定义二叉查找树的类。 2、 实验验证如下算法的正确性、各种功能及指标: 1)实现二叉查找树结构; 2) 实现二叉查找树的查找、插入和删除等算法; 第三次实验 题目1 邻接表存储的图相关算法的实验验证。 [实验目的] 验证邻接表存的图及其上的基本操作。 [实验内容及要求] 1、 定义邻接表存储的图类。 2、 实验验证如下算法的正确性、各种功能及指标: 1)创建一个邻接表存储的图; 2)返回图中指定边的权值; 3)返回图中某顶点的第一个邻接顶点; 4)返回图中某顶点关于另一个顶点的下一个邻接顶点的序号; 5)插入操作:向图中插入一个顶点,插入一条边; 6)删除操作:从图中删除一个顶点,删除一条边。 题目2 图的遍历算法的实验验证。 [实验目的] 验证图的遍历算法。 [实验内容及要求] 1、 定义邻接表存储的图。 2、 实验验证如下算法的正确性、各种功能及指标: 1)创建一个图; 2)图的深度优先遍历的递归算法; 3)图的深度优先遍历的迭代算法; 4)图的广度优先遍历算法。 第四次实验 折半插入排序,堆排序,快速排序 请阅读说明文档

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值