C++数据结构

销售波动统计

任务描述

本关任务:假定有一组数据记录着卖场每天(不超过30天)的销售额(假设都是整数),要求你统计并输出销售的波动情况,即计算每天相对于前一天的销售额的增减情况(即后项减前项的差值)。

例如连续5天的销售额分别是:23 56 12 76 43,则销售波动情况的输出为:33 -44 64 -33。

其中:33 = 56 - 23,-44 = 12 - 56,以此类推。

相关知识

一段时间内每天销售额的记录需要由一组数据来完成,其中,数据的个数可能为0到30中的任何一个数,尽管我们可以用30个变量来存储它们,但这显然很不方便。

使用数组则不仅简单直观,而且逻辑清晰。本关我们就一起来学习数组的运用。

一维数组

声明一维数组的一般形式为:

<元素类型>  <数组名>[<元素个数>];
  • <元素类型>指明了数组元素的类型,可以是整型、实型、布尔型和字符型等简单数据类型,也可以是用户定义的复合数据类型,包括数组类型
  • <数组名>由标识符充当,是整个数组的名字;<数组名>后面的方括号是必须的,C 和 C++把[]作为运算符处理,其优先级与括号相同,左结合;

方括号中的<元素个数>是整常量表达式,可以省略,表示数组元素的个数,注意数组元素的下标是从零开始计数的。

例如:

int a[10];

上述声明了一个数组名为 a 的整型数组,它有10个整型元素,下标的变化范围是0~9。

数组元素的访问

给定一个数组,我们可以采用以下形式访问数组里的元素:

<数组名>[<下标表达式>]
  • <下标表达式>是值为整型的表达式,它指明了拟访问的数组元素的下标。

例如,数组 a 的10个元素可以依次表示为:a[0] ,a[1] ,a[2] ,…… ,a[9]。

注意:如果访问数组元素时使用的下标超出了元素的数目,会导致访问越界错误。C 和 C++不检查访问越界,如果发生了访问越界,有可能导致程序在运行时发生错误。

数组的各元素按顺序存储在一片连续的内存单元中。数组int a[10]的内存分配见下图所示:

在这里插入图片描述

数组的初始化

数组初始化的方法是把初始值按顺序放在花括号中,数值之间用逗号分开。

例如:

int  a[10]={4};
float  r[20]={0.1, 5.1};
double  d[3]={10.0,5.0,1.0};

需要注意以下两点:

  1. 如果初始值的数目小于数组元素的数目,数组剩余的元素被自动初始化为 0。

例如:

int  n[5]={0};  //将数组 n 的所有数组元素都初始化为 0

注意:初始值的数目不能超过数组中元素的数目。

  1. 声明数组时可以省略数组元素的数目,这时系统会根据初始值的数目来确定数组元素的数目。

例如:

int  x[]={ 1, 2, 3, 4, 5 };

实际上声明了一个包含了5个整型元素的数组 x :x[0] …… x[4] ,其值分别被初始化为 1 ,…… ,5。

这里再强调一下:我们不能直接操作某个数组,只能对数组中的某一个元素进行操作。也就是说,数组需要“拆开来用”****,每次只能操作一个数组元素,操作数组元素的方法和操作普通变量的方法相同,“拆开数组”的方法就是数组名加下标访问数组元素的方式。

例如:

a[7]= 4;     // 给数组元素 a[7] 赋值
x = 2 * a[i+2];     // 数组元素作表达式的运算量

数组的遍历

对于数组而言,最常见的用法是使用循环语句来操作数组,对数组元素进行遍历和处理

例如下面的程序对数组 n 中的元素求和:

int n[10] = {12,34,55,71,1,65,423,19,540,10};     // 声明数组n并初始化
int i, sum = 0;     // 定义循环变量和累加和变量
// 循环遍历数组并求和
for (i = 0; i <= 10-1; i++)
{
    sum += n[i];
}
cout << "The summary is: " << sum << endl;     // 输出求和结果

编程要求

在右侧编辑器中的Begin-End之间补充代码,程序给出计算几天的销售额分并给出这些天的额分(数据由平台提供,已获取,直接使用即可),统计并输出销售的波动情况,即计算每天相对于前一天的销售额的增减情况(即后项减前项的差值)。

温馨提示:这里要求输出的每个数字之间用一个空格隔开,最后一个数字后面没有空格。

测试说明

平台会对你编写的代码进行测试,比对你输出的数值与实际正确数值,只有所有数据全部计算正确才能通过测试:

测试输入:8 34 56 234 87 43 98 109 234(第一个数据表示有 8 天的销售额分,剩下的数据为具体额分)
预期输出:22 178 -147 -44 55 11 125

测试输入:4 0 89 190 289
预期输出:89 101 99

#include <stdio.h>
#include <iostream>
using namespace std;

int main()
{
    int n, a[30], i;     // 定义变量及数组,n-销售额个数,a-销售额
    cin >> n;     // 输入销售额数量,n <= 30
    // 输入n个销售额,分别存入a[0]到a[n-1]
    for(i = 0; i < n; i++)
        cin >> a[i];
    // 请在此添加代码,计算并输出销售额的波动情况
    /********** Begin *********/
	for(int j= 0;j<n-1;j++)
        cout <<  a[j+1]-a[j]<<" ";
    cout << endl;
    
    /********** End **********/
    return 0;
}

最大销售增幅

任务描述

本关任务:编写函数 maxIncrease,用于计算一个销售额序列中的最大销售增幅并返回。这里的销售额都是非负整数。

对于给定的销售额序列 A,假设序列 A 的长度为 n( n >= 2 ),最大销售额增幅是指满足0 <= x <= y < nA[y] - A[x]的最大值。

例如,销售额序列11,3,5,7,9,2,4,6,8,10的最大增幅为8(在 x=5 , y=9 时)。

相关知识

数组作为函数的参数

由于数组的存储是内存中一块连续的单元,数组元素的个数也是不确定的(不同的数组可能包含不同个数的数组元素),因此函数传递数组需要传递两个信息,一个是数组的地址(数组名就是数组地址),告诉被调用函数该数组存放在内存什么地方,**另一个是数组元素个数,**告诉被调用函数访问该数组时允许的下标范围(防止访问越界)。

参数传入一个整型数组的函数的函数原型如下:

void  convertScores(int a[],  int len);

第一个参数 a 是一个数组类型的形式参数。该参数 a 接收传入数组的数组名,也就是数组的首地址,其后的方括号中的数组长度不需要,即使有,编译器也会将其忽略

**第二个参数 len 是整型参数。该参数 len 传入数组 a 的长度,**即 a 有 len 个数组元素,访问 a 的元素的下标范围就是 0 到 len-1。

例如:下面的程序将一个整型数组中存放的10名同学的百分制成绩转换为5分制,并输出。

#include <iostream>
using namespace std;
// 分数转换函数的函数原型,其中:s-学生成绩数组名,len-学生成绩个数
void convertScores(int s[], int len);
int main()
{
    // 定义学生成绩数组并初始化
    int scores[10] = {85, 63, 72, 52, 95, 82, 77, 69, 88, 73};
    convertScores(scores, 10);     // 调用分数转换函数convertScores
    // 转换完成后输出分数数组中的分数
    for(int i = 0; i < 10; i++)
        cout << "scores["<<i<<"] = " << scores[i] << endl;
    return 0;
}
// 函数convertScores
void convertScores(int s[], int len)
{
    // 逐个访问数组中的每个元素,并做转换
    for (int i = 0; i < len; i++)
        s[i] = s[i]/20;     // 修改 s[i] 实际上就是修改 main 函数中的 scores[i],因为 s 和 scores 拥有相同的地址值
}

程序的输出为:

scores[0] = 4
scores[1] = 3
scores[2] = 3
scores[3] = 2
scores[4] = 4
scores[5] = 4
scores[6] = 3
scores[7] = 3
scores[8] = 4
scores[9] = 3

上述示例中函数调用convertScores(scores, 10);导致数组 scores 中的每个元素都被转换了。也就是说,在被调用函数体内对数组 s(形式参数)的元素进行的修改操作实际上作用在了数组 scores(实在参数)的元素上,这是否就是传引用呢?

实际上,C 和 C++ 在处理数组参数时是严格地采取了传值方式。

参数传递数组的方式有比较特殊的地方,要理解传递数组的机制,首先要明确下面两点:

  1. 和一般变量一样,数组名也是有值的,数组名的值就是数组的第一个元素的地址(首地址);
  2. 和一般变量一样,数组作为函数参数进行传递时,传递的也是数组变量的值,即数组第一个元素的地址。

由于数组名的值就是数组的第一个元素的地址,而访问数组元素的方式是数组名加下标(也就是首地址加下标),才使得尽管采取的是传值方式,但在被调用函数 convertScores 中,通过 s 加下标的方式访问数组元素,实际上访问的就是 main 函数中定义的数组 scores 的数组元素,因为 s 和 scores 代表了同样的地址位置。

传数组就相当于传指针。

编程要求

在右侧编辑器中的Begin-End之间补充代码,计算一个销售额序列中的最大销售增幅并返回。具体要求如下:

1.如对于销售额序列 A ,假设序列 A 的长度为 n( n >= 2 ) ,最大销售额增幅是指满足 0<=x<=y<n 的A[y] - A[x]的最大值;

2.函数 maxIncrease 需要将数组作为参数传入,在该数组中计算最大销售额增幅并返回。

测试说明
平台会对你编写的代码进行测试,比对你输出的数值与实际正确数值,只有所有数据全部计算正确才能通过测试:

测试输入:6 12 34 78 45 10 23(第一个数据表示有6天的销售额分,剩下的数据为具体额分)
预期输出:最大销售增幅为:66

测试输入:9 90 2 80 3 70 6 50 7 40
预期输出:最大销售增幅为:78

#include <iostream>
using namespace std;

// 函数maxIncrease:计算销售额增幅
// 参数:s-销售额数组,n-销售额数组长度,n>1
// 返回值:销售额最大增幅
int maxIncrease(int s[], int n);

int main()
{
    int n, a[30], i;     // 定义变量及数组,n-销售额个数,a-销售额数组
    cin >> n;      // 输入销售额数量,n>1
    // 输入n个销售额,分别存入a[0]到a[n-1]
    for(i = 0; i < n; i++)
        cin >> a[i];
    i = maxIncrease(a,n);
    cout << "最大销售增幅为:" << i << endl;
    return 0;
}

int maxIncrease(int s[], int n)
{
    //请在此添加代码,实现函数maxIncrease
    /********** Begin *********/
    int num =0;
    for(int i = 0;i<n;i++)
    {
        int j =i+1;
        for(j;j<n;j++)
        {
            num = (num>(s[j]-s[i]))? num:(s[j]-s[i]);
        }
    }
    return num;
    
    /********** End **********/
}

猴子选大王

任务描述

本关任务:编写一个函数 king,实现猴子选大王的功能。

新猴王的选择方法是:让 N 只候选猴子围成一圈(最多100只猴子),从某位置起顺序编号为 1 ~ N 号。从第1号开始报数,每轮从1报到3,凡报到3的猴子即退出圈子,接着又从紧邻的下一只猴子开始同样的报数。如此不断循环,最后剩下的一只猴子就选为猴王。

相关知识

针对上述任务的描述,我们假设 N 只猴子分别占据数组 a 的下标从 1 ~ N 的位置(0号位置空着),从序号为1的猴子开始报数。返回值为猴王的序号。

本关可以把数组当作循环表来看,即第 N 个猴子的下一个猴子是第1个猴子。每次选出一个猴子,则对该位置进行标注,下次报数的时候跳过这些标注的位置即可。

在这里,我们假设 k 是当前报数猴子的序号,则下一个报数猴子的序号可以用如下程序求得:

k++;
if(k == N + 1)
    k = 1;

当有些猴子出局后,报数时就需要跳过空位。所以可以对每个位置进行标注,以区分有没有猴子。

例如为0时表示没有猴子,否则就是有。当有很多空位时,可以使用如下程序获得下一个猴子的位置:

// nx 是当前报数的序号,如果为 0 则找后面不为 0 的,否则就是它了
while(a[nx]==0)
{   // 为0时
    nx++;  // 看下一个位置的猴子
    if(nx == N + 1) // 到尾部了,再回到头部
        nx = 1;
}

编程要求

在右侧编辑器中的Begin-End之间补充代码,输出 N(数据由平台提供,已获取,直接使用即可) 只猴子中谁是猴子大王的。具体要求如下:

让 N 只候选猴子围成一圈(最多100只猴子),从某位置起顺序编号为 1 ~ N 号;

从第 1 号开始报数,每轮从 1 报到 3 ,凡报到 3 的猴子即退出圈子,接着又从紧邻的下一只猴子开始同样的报数;

如此不断循环,最后剩下的一只猴子就选为猴王。

测试说明

平台会对你编写的代码进行测试,比对你输出的数值与实际正确数值,只有所有数据全部计算正确才能通过测试:

测试输入:5
预期输出:4号猴子是大王。

测试输入:18
预期输出:14号猴子是大王。

#include <iostream>
using namespace std;

// 函数king:猴子选大王
// 参数:a-猴子数组n-1个猴子分别占据下标为~n-1的位置,n-数组长度
// 返回值:新猴王的下标序号
int king(int a[], int n);

int main()
{
    int n, a[1000], i;     // 定义变量及数组,n-猴子数量,a-猴子数组
    cin >> n;     // 输入猴子数量,n>0

    // 初始化猴子数组,n 个猴子分别占据 n 个位置
    a[0] = 0; // 0号位置没有猴子
    for(i = 1;i <= n; i++)
        a[i] = i;

    // 选大王啦
    i = king(a, n );
    cout << i << "号猴子是大王。" << endl;
    return 0;
}

int king(int a[], int n)
{
    // 请在此添加代码,实现函数king
    /********** Begin *********/
    int i,j,k,h,nx=1;  
    for(j=1;j<n;j++)
    {
    	
    	for(k=1;k<4;k++)
        {
    	
            for(;a[nx]==0;nx++)
    	    {
    		    if(nx==n&&a[nx]==0)
		        {
    	            nx=0;
    	        }
		    }
    	if(k==3)
    	{
    		a[nx]=0;
    		
		}		
    	nx++;
    	
    	if(nx == n+1)
    		nx=1;	
    	}
	}
for(h=1;h<n;h++)
{

	if(a[h]>0)
	    i=a[h];	
	}
return i;
}


犯二的程度

任务描述

本关任务:编写函数 silly,计算整数(不多于100位)犯二的程度并返回。

数字也可以“犯二”,一个整数“犯二的程度”定义为:该数字中包含2的个数与其位数的比值,如果这个整数是负数,则程度增加0.5倍,如果还是个偶数,则再增加1倍。

例如,数字−56782223456是个11位数,其中有3个2,是负数,也是偶数,则它的犯二程度为:3/11×1.5×2,约为0.818182。

相关知识

由于 C 和 C++ 中 int 类型的表示范围有限,为−2147483648~2147483647,所以超过10位的整数就会溢出。

例如下面的程序:

int a;
cin >> a;
cout << a << endl;
测试输入:77777777777

输出结果:-858993460

很显然整型变量 a 无法表示超出范围的数。

字符串数组

在 C 和 C++ 中,一个字符串就是用一对双引号括起来的一串字符。字符串的存储是利用一维字符数组来实现的,该字符数组的长度为待存字符串的长度加1。即如果一个字符串的长度为 n ,则用于存储该字符串的数组的长度应为 n+1。

当字符串存入数组时,是把每个字符依次存入到数组的对应元素中,即把第一个字符存入到下标为0的元素中,第二个字符存入到下标为1的元素中,依次类推,最后会把一个空字符 ′\0′ 存入到下标为 n 的元素中(这里假定字符串的长度为 n)。字符存储的是它的 ASCII 码或区位码。

例如用一维字符数组 a[12] 来存储字符串 ″Strings.\n″ 时,数组 a 中的内容参见下图:

在这里插入图片描述

接下来,我们可以利用字符串来初始化字符数组,如:

char a[10] = ″array″;
char b[20] = ″This is a pen.;
char c[8] = ″″;

第一条语句定义了字符数组 a[10] 并被初始化为“array”,其中 a[0] ~ a[5] 元素的值依次为字符‘a’, ‘r’,‘r’,‘a’,‘y’和‘\0’;

第二条语句定义了字符数组 b[20] ,其中 b[i] 元素( 0 ≤ i ≤ 13 )被初始化为所给字符串中的第 i+1 个字符,b[14] 被初始化为字符串结束标志符‘\0’;

第三条语句定义了一个字符数组 c[8] 并初始化为一个空串,此时它的每个元素的值均为‘\0’。

我们可以看到对于字符串而言,它的存储总是以 ‘\0’ 结尾。

例如下面的程序可以计算并输出字符串的长度:

char s[1000];     // 定义一维字符数组
cin >> s;     // 输入一个字符串存入字符数组s中
int i = 0;
// 逐个判断字符数组的某一位是否是'\0',该字符意味着字符串的结束
while(s[i] != '\0')
    i++;
cout << i << endl;     // 输出字符串的长度

获取字符对应的整数

由于使用字符形式存储数字时,存储的是数字对应的 ASCII 码,由于在 ASCII 码表中字符‘0’到字符‘9’是连续存放的,所以下面的程序可以获得字符对应的数字:

char c = '2';     // c 中存储的是字符 '2' 的ASCII码
int k = c - '0';     // k 即为字符 '2' 对应的整数 2

编程要求

在右侧编辑器中的Begin-End之间补充代码,计算整数(不多于100位,数据由平台提供)犯二的程度并返回。具体“犯二的程度”定义如下:

该数字中包含 2 的个数与其位数的比值;

如果整数是负数,则程度增加 0.5 倍;

如果整数是偶数,则再增加 1 倍。

提示:对于本关要求计算不超过100位的整数,很显然不能使用 int 类型,但可以把输入的一串数字当做字符串处理,使用字符数组来接收输入的整数。

测试说明

平台会对你编写的代码进行测试,比对你输出的数值与实际正确数值,只有所有数据全部计算正确才能通过测试:

测试输入:-13142223336
预期输出:0.818182

测试输入:122335468912334612
预期输出:0.444444

#include <iostream>
using namespace std;

// 函数silly:计算数值有多二
// 参数:a-存储数值的字符数组,以'\0'结束,所以不需要另一个参数告诉函数数组有多长
// 返回值:数值犯二的程度
double silly(char a[]);

int main()
{
    char s[102];     // 定义存储数值的数组
    cin >> s;     // 输入不超过位的整数
    double sy = silly(s);     // 计算犯二的程度
    cout << sy << endl;     // 输出犯二的程度
    return 0;
}

double silly(char *a)
{
    // 请在此添加代码,实现函数silly
    /********** Begin *********/
    int i,len,count_2=0,judge_np,judge_even;
	double result;

	i=0;
	while(a[i]!='\0')i++;
	len = i;

    for(i=0;i<len;i++){
		if(a[i]=='2'){
			count_2++;
		}
	}

    if(a[0]=='-'){
		judge_np=1;
	}else{
		judge_np=0;
	}

	if(a[len-1]%2==0){
		judge_even=1;
	}else{
		judge_even=0;
	}

	if(judge_np)
        len--;
	result = 1.0*count_2/len;
	if(judge_np)
        result*=1.5;
	if(judge_even)
        result*=2;
	return result;
    /********** End **********/
}

队列变换

任务描述

本关任务:编写函数 rotateLeft,该函数实现对一个n×n方阵中的每个元素循环向左移m个位置(0<m<n),即将第0、1、…… 、n−1列变换为第n−m、n−m+1、…… 、n−1、0、1、…… 、n−m−1列。

相关知识

多维数组 / 二维数组
C 和 C++ 中有多个下标的数组称为多维数组。具有两个下标表示的数组称为二维数组。

例如:

int a[3][4];
char c[4][3][5];

其中 a 是一个整型二维数组,c 是一个字符型三维数组。

数学上的二维矩阵可以看成其元素是向量的向量,也可以看成元素是一维数组的一维数组,所以二维矩阵可以用二维数组表示出来。

上述示例中的二维数组 a 有 2 个下标:

  • 第1个下标可以称为行,变化范围是0~2;

  • 第2个下标可以称为列,变化范围是0~3。

因此,a 共有3行4列,12个元素。每个数组元素用数组名和两个下标表示。

例如:a[1][2]和a[2][1]分别表示第1行第2列的元素和第2行第1列的元素(从第0行0列开始)。

二维数组 a 的逻辑结构如下图所示:

在这里插入图片描述

多维数组在内存中“按行”存放,越靠后的下标先变化,越靠前面的下标后变化。二维数组 a 在内存中的物理存储形式如下图所示:

在这里插入图片描述

二维数组的初始化

二维数组可以在声明时初始化。

例如,下面是对2∗2的二维数组 matrix 的初始化:

int  matrix[2][2]{ {1, 2}, {3, 4} };

数组元素的值用花括号按行分组,上述示例中将整常数1和2赋给了matrix[0][0]和matrix[0][1],将整常数3和4赋给了matrix[1][0]和matrix[1][1]。

特别注意以下两点:

  1. 如果指定行没有足够的初始值,与一维数组类似,则该行的剩余元素初始化为0,如果初始值只给出了部分行,则剩余的行中的所有元素都被初始化为0;

  2. 如果初始值之间没有用花括号按行分组,那么编译器会自动用初始值顺序初始化第0行的元素、第1行的元素、……。如果初始值的数目少于数组元素的数目,剩余的元素自动初始化为0。

例如:

int matrix[3][4]={ 1, 2, 3, 4, 5, 6 };
初始化完成后,数组 a 的各元素的值为:

matrix[0][0] = 1 ,
matrix[0][1] = 2 ,
matrix[0][2] = 3,
matrix[0][3] = 4 ,
matrix[1][0] = 5 ,
matrix[1][1] = 6 ,
其余元素均为 0 。

二维数组的操作

二维数组的典型操作方法:使用两重循环对数组进行遍历,并逐个元素进行操作。两重循环分别遍历两个下标的范围。

例如:下面的程序可以遍历二维数组并输出每一个元素的值。

// 定义并初始化二维数组m

int m[4][4] = { {1, 1, 1, 1},
                {2, 2, 2, 2},
                {3, 3, 3, 3},
                {4, 4, 4, 4}};

// line遍历第一维
for (int line = 0; line < 4; line++)
{
    // col遍历第二维,输出某一行
    for (int col = 0; col < 4; col++)
        cout << "\t" << a[line][col];
    // 输出完一行后换行
    cout << endl;
}

温馨提示:如果循环左移多个位置不好处理,可以用循环,每次循环左移一个位置。

编程要求

在右侧编辑器中的Begin-End之间补充代码,实现队列变换的功能。具体要求如下:

队列为 n×n;

每个元素循环向左移m个位置(0<m<n),即将第0、1、…… 、n−1列变换为第n−m、n−m+1、…… 、n−1、0、1、…… 、n−m−1列。

提示:存储n×n的方阵需要使用二维数组。

测试说明

平台会对你编写的代码进行测试,比对你输出的数值与实际正确数值,只有所有数据全部计算正确才能通过测试:

测试输入:

3 1
1 2 3
4 5 6
7 8 9
预期输出:

2 3 1
5 6 4
8 9 7
测试输入:

4 2
1 2 3 4
2 3 4 5
3 4 5 6
4 5 6 7
预期输出:

3 4 1 2
4 5 2 3
5 6 3 4
6 7 4 5

#include <iostream>
using namespace std;

// 函数rotateLeft:矩阵循环左移
// 参数:a-100*100的二维数组,用来存储n行n列的数组(n<100),存储在其~n-1行和~n-1列,
// m-循环左移的位数(0<m<n)
// 返回值:无,循环左移的结果写入原二维数组中
// 说明:传递多维数组时,形式参数中,除了第一维外,其它维的大小必须给出
// 方便编译器计算每个数组元素的地址
void rotateLeft(int a[][100],int n,int m);

int main()
{
    int a[100][100];     // 定义存储二维数组的空间
    int n, m;
    cin >> n >> m;     // 输入n和m

    // 输入n*n的矩阵,存储在数组a的~n-1行和~n-1列
    int i, j;
    for(i = 0; i < n; i++)
        for(j = 0; j < n; j++)
            cin >> a[i][j];

    // 循环左移
    // 说明:传递多维数组时,实在参数只需要给出数组名就可以了
    rotateLeft(a,n,m);

    // 输出循环右移的结果
    for(i = 0; i < n; i++)
    {
        for(j = 0; j < n; j++)
            cout << " " << a[i][j];
        cout << endl;
    }
    return 0;
}

void rotateLeft(int a[][100],int n,int m)
{
    // 请在此添加代码,实现函数rotateLeft
    /********** Begin *********/
    int i,j,k,t;
    for(k=0;k<m;k++)
	{
    	for(i=0;i<n;i++)
	    {
			t=a[i][0];
	        for(j=0;j<n;j++)
	        {
	            a[i][j]=a[i][j+1];
	        }
	        a[i][j-1]=t;
	    }
	}

    
    
    /********** End **********/
}

朋友圈点赞

任务描述

朋友圈有一个点赞功能,你可以为你喜欢的文章点赞表示支持。每篇文章都可以根据其内容给出一些标签,这些标签用数字表示(如:1 代表心灵鸡汤、2 代表政治、3 代表经济、…),你点赞的文章的类型,也间接反应了你的喜好。

本关任务:编写一个完整的程序,通过统计一个人点赞的纪录,分析这个人的喜好。

相关知识

本关的程序实现可以边输入,边统计每个标签出现的次数,标签最多可能会有1000个,用数组很合适。统计完之后,再查找其中出现次数最多的最大标签即可。

编程要求

程序的输入格式为:

4
3 889 233 2
5 100 3 233 2 73
4 3 73 889 2
2 233 123
输出结果为:233 3

上述示例中出现最多次(3次)的标签有233和2,存在并列时,输出最大的那个标签233,然后输出其出现的次数3。

测试说明
平台会对你编写的代码进行测试,比对你输出的数值与实际正确数值,只有所有数据全部计算正确才能通过测试:

测试输入:

4
3 889 233 2
5 100 3 233 2 73
4 3 73 889 2
2 233 123
预期输出:233 3

#include<iostream>
using namespace std;
int a[1001]={0};
int main()
{
 int n,m,x,mnum=0,num;//mnum用来计数记录最大值 
 //读取数据 
 cin>>n;
 while(n--)
 {
  cin>>m;
  for(int i=0;i<m;i++)
  {
   cin>>x;
   a[x]++;
  }
 }
 for(int i=0;i<=1000;i++)
 {
  if(a[i]>=mnum)
  {
   mnum=a[i]; 
   num=i;
  }
  } 
 cout<<num<<" "<<mnum;
 return 0;
}
 

去掉字符串首尾空格

任务描述

本关任务:文本匹配的时候经常会因为空格个数的不同而匹配失败,现在要求你编写程序对输入的字符串进行处理,去掉首尾的空格。

例如:输入的字符串是“__abcd ”,去掉首尾空格后字符串变为“abcd”。

相关知识

指针是 C 语言中的一个重要概念,也是 C 语言的一个重要特色。在 C 语言中,指针被广泛使用,它和数组、字符串、函数间数据的传递等有着密不可分的联系。可以说,没有掌握指针就没有掌握 C 语言的精华。

指针可以对内存中各种类型的数据进行快速、直接的处理,也可以为函数间的数据传递提供简洁、便利的方法。正确熟练地使用指针有利于编写高效的程序,但指针使用不当也容易导致严重错误。指针很灵活,也很危险。

定义指针变量

指针变量(简称指针)就是存放地址的变量。其声明形式与一般变量声明相比只是在变量名前多一个星号*,接下来看两个例子。

例1:

int *p;

该例中声明了变量 p 为指向整型值的指针(即变量 p 中可以存放某个整型变量的地址)。这里的*在声明语句中,是指针说明符,表示声明的变量是指针变量。

例2:

float *xPtr, *yPtr, f;

该例子中声明了两个指向浮点型值的指针 xPtr 和 yPtr 以及一个浮点型变量 f 。

指针的性质

① 指针可以赋值为 NULL 或某个地址。具有值 NULL 的指针不指向任何地址;

② 指针是具有特定属性的地址。光有地址只是知道数据存储在内存的某个位置,但怎么访问该位置的数据(即访问多少位,以什么方式访问)还需要指针类型来明确

例如:使用 int 类型的指针访问其所指向的数据时,会一次性读取32位( int 类型的数据是32位),并使用整型数据的格式访问该数据。所以指针不仅仅是一个地址,还必须有特定的属性(类型)。

数组名可以看成是一个特殊的地址,首先数组名是地址(数组的首地址),其次数组名有属性(数组元素的类型)所以可以把数组名赋值给同类型的指针变量。

例如:

char s[10] = "China";
char *sptr = s;

第二条语句赋值后 sptr 拥有和 s 同样的值,即数组的首地址,也就是存储字符‘C’的单元的地址。

要访问指针所指向的单元可以使用间接引用运算符*(不同于前面声明语句中的*,这里的*在表达式中,是运算符),*也被称为复引用运算符,它返回其操作数(指针)所指向的对象;

例如:

**char s[10] = “China”;
char sptr = s;
cout << sptr;

将输出指针 sptr 所指的单元中存储的字符(因为 sptr 是 char 类型的指针),也就是输出字符‘C’。

可以通过指针的复引用修改指针所指向的单元;

例如:

char s[10] = "China";
char *sptr = s;
*sptr = 'c';

上述代码会将该存储单元中大写字符的‘C’修改为小写字符的‘c’。

请注意前面代码的输出语句和下面的代码语句的区别:

char s[10] = "China";
char *sptr = s;
cout << sptr;

这条语句将输出字符串“China”。之前学习字符数组时应该知道,语句cout << s;会输出数组 s 中存储的整个字符串,实际上 C++ 在使用 cout 输出 char 类型指针时,不是输出字符指针的值(地址),而是输出从该地址开始的字符串(逐个输出一个个字符,直到碰到 ‘\0’ 为止)。所以cout << sptr;和cout << s;的作用一样,都是输出字符串“China”。

访问一个字符串一般也是使用该字符串的首字符的地址;

指针也可以参与算术运算。指针加上或减去一个整数 n,其运算结果是指向指针当前指向变量的后方或前方的第 n 个变量。

例如:之前 sptr 指向字符‘C’的存储单元,执行语句sptr++;后 sptr 则指向字符‘h’的存储单元。如下语句:


while(*sptr != ‘\0)
    sptr++;

//则可以使指针 sptr 指向该字符串后面的 '\0'。如果要输出字符串中的部分内容,也可以通过修改指针实现,如:
char s[10] = “China”;
char *sptr = s;
sptr++;
cout << sptr;

上述代码执行语句sptr++;后,指针 sptr 指向了字符 ‘h’ 的存储单元,此时cout << sptr;输出的是 sptr 指向的字符串,即“hina”。
同类型的两个指针可以参与各种关系运算,其结果可以反映两指针所指向的地址之间的位置前后关系。
例如:
i

nt a[10];
int *p = a, *q = &a[1];
if(p > q)
    cout << “p>q” << endl;
else
    cout << “p<=q” << endl;

上述代码中指针 p 中存放的是 a 的值,也就是 a[0] 的地址,q 中存放的是 a[1] 的地址,而数组元素是按序连续存储的,所以 q 的值要比 p 的值大,程序输出p<=q。

编程要求

在右侧编辑器中的Begin-End之间补充代码,以实现去掉字符串(字符串由平台提供,且已在主函数中获取)首尾空格的功能。具体要求如下:

对于输入的字符串,去掉该字符串首尾的空格后输出;

字符串的存储使用一维字符数组,但本关要求使用指针操作。写完程序后,你也会发现使用指针实现会容易得多。

测试说明

平台会对你编写的代码进行测试,比对你输出的数值与实际正确数值,只有所有数据全部计算正确才能通过测试:

为了方便显示空格,下面给出的测试输入使用下划线 ‘_’ 表示空格。

测试输入:_____asd
预期输出:asd

测试输入:_a_b_c_d
预期输出:a_b_c_d

#include <iostream>
using namespace std;


char * trim(char * str);

int main()
{
    char s[1024];     // 定义存储字符串的一维字符数组
    // 输入一行字符,可以包含空格
    // 输入的字符串存入s中,最多读取个字符,后面自动加上'\0'
    cin.getline(s,1024);
    cout << trim(s) << endl;     // 输出去掉首尾空格后的字符串
    return 0;
}

// 函数trim:去掉字符串首尾空格
// 参数:str-字符指针,指向输入的字符串
// 返回值:字符指针,指向去掉首尾空格后的字符串(首地址)
// 提示:可以直接在字符串str中操作
char * trim(char * str)
{
    // 请在此添加代码,实现函数trim
    /********** Begin *********/
    char* point = str;//
    while (*point != '\0')
    {
        point++;
    }
    point--;
    while (point >= str && *point == ' ')
    {
        *point = '\0';
        point--;
    }
    point = str;
    while (*point == ' ')
    {
        point++;
    }
    return point;
    
    /********** End **********/
}

用指针实现pswap函数

任务描述

本关任务:使用指针实现两个整数值的交换。

相关知识

用指针传递参数
C 和 C++ 函数调用的参数传递方式有两种:传值和传引用。

传值只是值的传递,被调用函数则无法修改实在参数的值;

传引用则是实参和形参共享实在参数的存储单元,所以被调用函数可以通过修改形参来修改实参的值。

如果采用传值的方式传递指针值,可以实现类似于传引用的效果。

例如:

#include <iostream>
using namespace std;
// 函数inc:将p指向的整数值加
// 参数:p-int类型指针,指向要加的整数
void inc(int * p)
{
    (*p)++;     // *p 访问 p 指向的单元,++ 将该单元的数据加
    // 注意不能是 *p++, 因为 * 和 ++ 优先级相同,且右结合,这种写法修改的是 p 的值,而不是 *p 的值
}
int main()
{
    int a = 10;
    inc(&a);     // 调用 inc 函数,修改 a 的值(传递的是 a 的地址)
    cout << a << endl;     // 输出 a 的值
    return 0;
}

上述程序的输出为11,其中被调用函数 inc 只修改了 main 函数中的局部变量 a 的值,但并没有修改实参的值(实参是&a,即 a 的地址依然没变)。

#include <iostream>
using namespace std;

void pswap(int * p, int *q);

int main()
{
    int a, b;
    cin >> a >> b;     // 输入两个整数
    pswap(&a,&b);     // 调用pswap函数,交换a、b的值
    cout << a << " " << b << endl;     // 输出a、b的值
    return 0;
}

//函数pswap:交换指针p和q指向的单元中的整数值
//参数:p,q-int类型指针,指向要交换的整数
void pswap(int * p, int *q)
{
    // 请在此添加代码,实现函数pswap
    /********** Begin *********/
    int temp = *p;
    *p = *q;
    *q=temp;
    
    
    /********** End **********/
}

选出串中的数字

任务描述

本关任务:在一串隐藏着一个或多个数值的字符中,选出字符串中的所有数字字符,并将选出的数字字符重新组成新字符串。如果在第一个数字字符之前有负号,则保留该负号,有多个负号时只保留一个。

例如:输入的字符串为“a-1-2-4sd5 s6”,抽取数字后得到的新字符串为“-12456”。

提示

如果有字符指针 p 和 q,则下面的程序可以实现:当 q 指向的字符是数字时,将 q 指向的数字拷贝到 p 指向的存储单元:

if(*q >= ‘0’ && *q <= ‘9’)
{
*p = *q;
}

#include <iostream>
using namespace std;

void extractNum(char * str);

int main()
{
    char s[1024];
    cin.getline(s,1024);     // 输入一行字符
    extractNum(s);     // 调用extractNum函数,选出数字
    cout<<s<<endl;     // 输出选出的数字
    return 0;
}

// 函数extractNum:选出str指向的字符串中的数字,并写回str
// 参数:str-指向字符串
void extractNum(char * str)
{
    // 请在此添加代码,实现函数extractNum
    /********** Begin *********/
    char *p=str,*q=str;//s的首地址
 	int f=1;
 	while(*q!='\0')
 	{
  		if(*q=='-'&&f)
  		{
   			*p++=*q++;
   			f=0;
  		}
  		else if(*q>='0'&&*q<='9')
  		{
   			f=0;
   			*p++=*q++;
  		}
  		else
  		{
   			q++;
  		}
 	}
 	*p='\0';
    /********** End **********/
}

大写字母好看

任务描述

本关任务:将一个字符串中的所有小写字母变成对应的大写字母,其它字母不变。

例如:对字符串“abc12! ef”执行函数后输出结果为“ABC12! EF”。

提示

在 ASCII 码表中,大写字母‘A’到‘Z’是连续编码的,小写字母‘a’到‘z’也是连续编码的。也就是说‘A’和‘a’的 ASCII 码的差与‘Z’和‘z’的 ASCII 码的差是一样的。

所以如果字符变量 ch 中存放的是小写字母,则下面的语句可以将其变成对应的大写字母:

ch = ch + ‘A’-‘a’;

#include <iostream>
using namespace std;

void toUp(char * str);

int main()
{
    char s[1024];
    cin.getline(s,1024);     // 输入一行字符
    toUp(s);     // 调用toUp函数,转换成大写字母
    cout<<s<<endl;     // 输出变更后的新字符串
    return 0;
}

// 函数toUp:将str指向的字符串中的小写字母变成对应的大写字母
// 参数:str-指向字符串
void toUp(char * str)
{
    // 请在此添加代码,实现函数toUp
    /********** Begin *********/
    char *q = str,*p = str;
    while(*q!='\n')
    {
        if(*q<='z'&&*q>='a')
        {
            *p = *p +'A'-'a';
            p++;
            q++;
        }
        else
        {p++;
        q++;}
    }
    
    
    /********** End **********/
}

子串出现的次数

任务描述

本关任务:计算一个字符串(子串)在另一个字符串(长串)中出现的次数。

相关知识

字符串操作函数
C 和 C++ 提供了一系列操作字符串的函数,要使用这些函数只要在代码的头文件部分包含 string.h 即可。

常用的字符串处理函数见下表:

函数原型函数功能
char * strcpy(char *dest,const char *src)将字符串 src 复制到 dest
char * strcat(char *dest,const char *src)将字符串 src 添加到 dest 末尾
char * strchr(const char *s,int c)检索并返回字符 c 在字符串 s 中第一次出现的位置
int strcmp(const char *s1,const char *s2)比较字符串 s1 与 s2 的大小,若 s1 串大于 s2 串则返回一个大于 0 的值;若 s1 串等于 s2 串则返回值为 0;若 s1 串小于 s2 串则返回一个小于 0 的值。
size_t strlen(const char *s)返回字符串 s 的长度
char * strncat(char *dest,const char *src,size_t n)将字符串 src 中最多 n 个字符复制到字符串 dest 中
int strncmp(const char *s1,const char *s2,size_t n)比较字符串 s1 与 s2 中前 n 个字符
char * strncpy(char *dest,const char *src,zise_t n)复制 src 中的前 n 个字符到 dest 中
char * strstr(const char *s1,const char *s2)扫描字符串 s1,并返回第一次出现 s2 的位置
char * strtok(char *s1,const char *s2)检索字符串 s1,该字符串 s1 是由字符串 s2 中定义的定界符所分隔

strstr 函数
在一个长串中查找子串可以使用strstr函数,该函数的函数原型为:

char* strstr(const char* s1, const char* s2);

该函数从 s1 所指字符串中第一个字符起,顺序向后查找出与 s2 所指字符串相同的子串,若查找成功则返回该子串首次出现的首地址,否则返回 NULL。

例如:

char *a="abcdeabcde";
char *b="bcd";
cout<<strstr(a,b)<<endl;

该程序输出结果为“bcdeabcde”,因为strstr(a, b)的返回值为“bcd”在“abcdeabcde”中第一次出现的首地址,所以用 cout 输出时,从该位置的字符开始,逐个输出直到 ‘\0’,即字符串“bcdeabcde”。

当然,查找子串时,也可以从长串的某个位置开始。

例如:

char *a="abcdeabcde";
char *b="bcd";
cout<<strstr(a+4,b)<<endl;

该程序的输出为“bcde”。因为a+4得到一个新地址,即 a 指向的字符串中第一个字符‘e’的地址,从该位置开始查找 b 指向的字符串‘bcd’,得到从字符‘e’开始的第一个“bcd”出现的地址,然后用 cout 输出该地址开始的字符串,即“bcde”。

strlen 函数

另外,下次从什么地方开始查找子串?应该是上次找到子串的开始位置加上子串的长度。其中,函数strlen可以计算字符串的长度,其函数原型为:

int strlen(const char *s);

函数 strlen 只有一个参数 s,它是一个字符指针,代表了一个字符串,函数计算 s 指向字符串的长度并返回。

例如:

char *a="x";
char *b="Hello world!";
cout<<strlen(a)<<endl;  // 输出 1
cout<<strlen(b)<<endl;  // 输出 12

编程要求

在右侧编辑器中的Begin-End之间补充代码,计算一个字符串(子串)在另一个字符串(长串)中出现的次数。

提示

一个子串在另一个长串中出现的次数是指长串中可以同时截取出多少个子串。例如:子串 “aaa” 在 “aaaaaaa” 中出现的次数为 2 次。

// 包含字符串函数库
#include <string.h>
#include <iostream>
using namespace std;

int frequency(char * substr, char * str);

int main()
{
    char sub[128],str[1024];
    cin.getline(sub,128);     // 输入子串
    cin.getline(str,1024);     // 输入长串
    int n = frequency(sub,str);     // 调用frequency函数,计算子串在长串中出现的次数
    cout<<n<<endl;     // 输出次数
    return 0;
}

// 函数frequency:计算子串在长串中出现的次数
// 参数:substr-指向子串,str-指向长串
// 返回值:出现的次数
int frequency(char * substr, char * str)
{
    // 请在此添加代码,实现函数frequency
    /********** Begin *********/
    int num =0;
    char* p =str;
    
    
    while(strstr(p, substr)!= NULL)
    {
        num++;
        p =strstr(p, substr);
        p = p+ strlen(substr);
    }
    return num;

    
    /********** End **********/
}

字符串的部分复制

任务描述

本关任务:实现字符串的部分复制。

提示

函数 strmncpy 的原型为:

void strmncpy(char *s, int m, int n, char *t);

参数 s 指向源字符串,t 指向字符串复制的目标单元,函数功能为将 s 指向字符串从第 m 个(从0开始编号)字符开始的连续 n 个字符复制到 t 指向的存储单元;

如果第 m 个字符后面的字符数不足 n 个,则复制到 ‘\0’为止;

如果 s 的长度不到 m,则复制空串。

例如:

char *s = "abcdefghijklmn";
char t[20];
strmncpy(s,4,6,t);
cout << t << endl;

输出结果为:efghij

#include <iostream>
#include <string.h>
using namespace std;

void strmncpy(char *s, int m, int n, char *t);

int main()
{
    char s[128],t[128];
    int m,n;
    cin>>s;     // 输入源串
    cin>>m>>n;     // 输入m和n
    strmncpy(s, m, n, t);     // 字符串复制
    cout << t <<endl;     // 输出复制结果
    return 0;
}

// 函数strmncpy:字符串的部分复制,将s指向字符串从第m个字符开始的n个字符复制的t中
// 参数:s-指向源字符串,t-指向目标字符串,m-起始位置,n-字符个数
// 返回值:无
void strmncpy(char *s, int m, int n, char *t)
{
    // 请在此添加代码,实现函数strmncpy
    /********** Begin *********/
    char* p = s;//这个用来跑
    char* q = t;//这个用来存
    int a = 0;//用于执行循环的次数
    p = p + m;//直接把地址改到目标数组的目标位置上去,最简单
    while (a < n)
    {
        *q++ = *p++;//把符合的值赋到用来存的指针这边
        a++;
    }
    *q = '\0';//结束,输出*q

    /********** End **********/
}

有理数化简

任务描述

本关任务:化简有理数。

相关知识

有理数的精确表示一般采用分数形式。分数的分子和分母都是整数,如果有理数为负数,则分子为负数,分母始终为正数。有理数的化简其实就是使分数成为最简分数,即通过化简使分子和分母互质。当分子为0时,分母应为1。

我们可以用两个整型变量来表示一个分数,但由于没有表示两个变量之间的逻辑关系,程序的可读性会很差。更好的方法是使用 C 和 C++ 的结构来表示。

结构
结构定义的一般形式为:

struct <结构名>{
    <成员列表>
};
  • struct 是关键字,表示结构定义;
  • <结构名>是一个标识符,是结构的名字;
  • <成员列表>由若干个成员的声明组成,每个成员都是该结构的一个组成部分。

例如,分数包含两个成员:分子和分母,可以定义为一个结构。分数结构名可以取名为 rationalNumber ,两个成员都是整型变量,可以分别取名为 fenzi 和 fenmu,则分数结构可以定义为:

struct rationalNumber{
    int fenzi;     // 分子
    int fenmu;     // 分母
};

结构的定义是定义一个新的数据类型(新类型名就是结构名),这样不会有内存分配。之后就可以像使用预定义类型(如 int )那样来使用结构类型。

例如:

rationalNumber x,y;

该语句声明了两个 rationalNumber 类型的变量 x 和 y,每个变量中都包含了两个成员:整型的 fenzi 和 fenmu。变量的声明会有内存分配,可以使用 x 和 y 的两个整型变量成员来存储数据,这样就可以把同一个分数的分子和分母存入 rationalNumber 类型的相应变量中,该变量就可以表示一个分数了。

结构的定义是可以嵌套使用的。例如 person 结构中包含生日成员,而生日中又包含年、月、日三个成员,代码如下:

// 定义结构date
struct date
{
    int year, month, day;
};
// 定义结构person
struct person
{
    int IDNumber;     // 身份证号
    char name[20];     // 姓名
    char sex;      // 性别
    date birthday;     // 生日,嵌套定义
};

结构的初始化及使用

结构变量的初始化和数组变量的初始化一样,采用花括号括起来的初始值列表的形式。

例如:

rationalNumber x={12,46};

该语句中,其初始化值按序赋值给各个成员,即12和46分别赋值给了 x 的 fenzi 和 fenmu 成员。

结构变量的使用有两种方式:一种是把结构变量当做一个整体使用;另一种是拆开了用,单独操作其中的某个成员:

  • 当结构变量当做一个整体使用时,有两个运算符:赋值运算符 = 和地址运算符 & 可以直接作用于结构变量上;

例如:

rationalNumber x = {12,46}, y ;
y = x ;     // 结构变量中的每个成员分别赋值
rationalNumber *p = &x;      // 声明结构指针,并初始化为 x 的地址
  • 当结构变量拆开使用时,需要访问结构变量的成员,并可以像使用普通变量那样来使用结构变量的成员,如分数的两个成员就可以看成普通的 int
    类型变量。

其中:拆开结构的方式有两种,一种是使用结构名 + 圆点运算符(.);另一种是使用指向结构变量的指针 + 箭头运算符(->)。

例如:

rationalNumber x= {12,46}, y ;
y = x ;     // 结构变量中的每个成员分别赋值
rationalNumber *p = &x;     // 声明结构指针 p,并让 p 指向 x
x.fenzi = 18;     // 使用圆点操作符访问并修改 x 的成员 fenzi
p->fenmu = 108;     // 使用箭头操作符并修改 x 的成员 fenmu

编程要求

在右侧编辑器中的Begin-End之间补充代码,对输入的有理数(数据由平台提供,已在主函数中获取)进行化简。

#include <iostream>
using namespace std;

struct rationalNumber{
    int fenzi; // 分子
    int fenmu; // 分母
};

// 函数reduction:有理数化简,对传入的有理数n进行化简
// 参数:n-有理数
// 返回值:无化简后的有理数
rationalNumber reduction(rationalNumber n);

int main()
{
    char c;
    rationalNumber x, y;
    cin >> x.fenzi >> c >> x.fenmu;   // 输入有理数,首先读入分子,然后是/,最后是分母
    y = reduction(x);   // 有理数化简
    // 输出化简的结果
    if(y.fenmu == 1)
        cout << y.fenzi << endl;
    else
        cout << y.fenzi << "/" << y.fenmu << endl;
    return 0;
}

rationalNumber reduction(rationalNumber n)
{
    // 请在这里补充代码,实现函数reduction
    /********** Begin *********/
    rationalNumber m = n;
    int a;
    while(m.fenmu!=0)
    {
        a=m.fenzi%m.fenmu;
        m.fenzi=m.fenmu;
        m.fenmu=a;
    }
    m.fenzi = (m.fenzi>0)? m.fenzi : -m.fenzi;
    n.fenzi = n.fenzi/m.fenzi;
    n.fenmu = n.fenmu/m.fenzi;

    return n;

    
    
    /********** End **********/
}

有理数加法

任务描述

本关任务:实现两个有理数的相加并返回结果且返回的结果必须是最简分数。

相关知识

函数不仅能通过参数传入结构变量,也可以通过返回值返回结构变量。

例如:下面的 add 函数将分数 x 的分子和分母分别加1后返回:

#include <iostream>
using namespace std;
struct rationalNumber{
    int fenzi;     // 分子
    int fenmu;     // 分母
};
rationalNumber add(rationalNumber x)
{
    x.fenzi++;     // 分子加 1
    x.fenmu++;     // 分母加 1
    return x;     // 返回结果
}
int main()
{
    rationalNumber a ={23,56}, b ;
    b=add(a);     // 调用add 函数
    cout << b.fenzi << "/" << b.fenmu << endl;     // 输出返回值
    return 0;
}

输出结果为:24/57。

温馨提示:结构变量作为一种新的数据类型,可以和预定义类型一样使用。

#include <iostream>
using namespace std;

struct rationalNumber{
    int fenzi; // 分子
    int fenmu; // 分母
};

// 函数rnAdd:两个有理数相加
// 参数:x,y-两个有理数
// 返回值:x+y的最简分数形式
rationalNumber rnAdd(rationalNumber x, rationalNumber y);

int main()
{
    char c;
    rationalNumber x, y, z;
    // 输入两个有理数
    cin >> x.fenzi >> c >> x.fenmu;
    cin >> y.fenzi >> c >> y.fenmu;
    z = rnAdd(x,y);     // 有理数相加
    // 输出相加的结果
    if(z.fenmu == 1)
        cout << z.fenzi << endl;
    else
        cout << z.fenzi << "/" << z.fenmu << endl;
    return 0;
}

// 请在此添加代码,实现函数rnAdd
/********** Begin *********/
rationalNumber rnAdd(rationalNumber x, rationalNumber y)
{
    rationalNumber m;
    m.fenmu = x.fenmu*y.fenmu;
    m.fenzi = x.fenzi*y.fenmu+y.fenzi*x.fenmu;
    rationalNumber n = m; 

    int a;
    while(m.fenmu!=0)
    {
        a = m.fenzi%m.fenmu;
        m.fenzi=m.fenmu;
        m.fenmu=a;
    }
    m.fenzi=(m.fenzi>0)? m.fenzi:-m.fenzi;
    n.fenzi/=m.fenzi;
    n.fenmu/=m.fenzi;

    return n;

 
    
}
/********** End **********/



有理数平均数

任务描述

本关任务:计算n(n<100)个有理数的平均数并返回,要求输出结果为最简分数形式。

相关知识

结构数组

结构的定义是定义一个新的数据类型,所以可以使用结构名(类型)来声明结构数组。结构数组的每一个数组元素都是一个结构变量。

使用结构数组时,首先像使用数组那样,用数组名加下标的方式访问数组元素,数组元素即结构变量,然后再访问结构变量的成员(使用圆点运算符或者箭头运算符),结构变量的成员则可以当普通变量使用。

下面的程序定义了结构 student,声明了 student 类型的数组 cs( cs 包含5个元素,分别是 cs[0] 、cs[1] 、cs[2] 、cs[3] 、cs[4] ),并同时对数组 cs 进行了初始化:

struct student {
    int num;
    char name[20];
    float score;
}cs[5]={{110, ″Zhang Ping″, 45},
        {102, ″Li Xiaoming″, 92},
        {153, ″Wang Ming″, 52.5},
        {134, ″Cheng Ling″, 87},
        {105, ″Wang Xiaofang″, 95},
};

由于 cs 是数组,所以初始化语法为花括号括起来的逗号分开的5个数据,由于 cs 数组的每个元素都是结构变量,所以对结构变量的初始化语法为花括号括起来的各个成员的值。

第一个花括号部分{110, ″Zhang Ping″, 45}初始化给 cs[0] ,其中110给了cs[0].num,“Zhang Ping”给了cs[0].name(其实cs[0].name也是一个数组,也是逐个数组元素初始化,‘Z’给了cs[0].name[0],…… ),45则给了cs[0].score。

访问结构数组成员

要使用结构数组,也需要拆开了使用,数组使用循环遍历,用[]加下标访问其数组元素,结构变量则使用圆点运算符拆开即可。

如下面的程序输出了数组 cs 的所有信息:

for(int i = 0; i < 4; i++)
{
    cout<<cs[i].num<<", "<<cs[i].name<<": "<<cs[i].score<<endl;
}

编程要求

在右侧编辑器中的Begin-End之间补充代码,计算n(n<100)个有理数(数据由平台提供,已在主函数中获取)的平均数并返回,结果为最简分数形式;

#include <iostream>
using namespace std;

struct rationalNumber{
    int fenzi; // 分子
    int fenmu; // 分母
};

// 函数rnMean:计算n个有理数的平均数
// 参数:a-存放有理数的数组,n-有理数的个数
// 返回值:n个有理数的平均数
rationalNumber rnMean(rationalNumber a[], int n);

int main()
{
    char c;
    rationalNumber a[100],z;
    int n, i;
    cin >> n;     // 输入有理数个数
    // 输入n个有理数
    for(i = 0; i < n; i++)
        cin >> a[i].fenzi >> c >> a[i].fenmu;
    z = rnMean(a,n);     // 计算有理数平均数
    // 输出平均数
    if(z.fenmu == 1)
        cout << z.fenzi << endl;
    else
        cout << z.fenzi << "/" << z.fenmu << endl;
    return 0;
}

// 请在此添加代码,实现函数rnMean
/********** Begin *********/
struct rationalNumber reduction(struct rationalNumber n)
{
int temp;
    int k,j;
    int max,min;
if(((n.fenzi)<0)&&((n.fenmu)>0))   // 分了几类,当然也能abs()是对整数取绝对值,而fabs()是对浮点数取.
{
    j = -n.fenzi;
    k = n.fenmu;
}
else if(((n.fenmu)<0) &&((n.fenzi)>0))
{
    j = n.fenzi;
    k = -n.fenmu;
}
else
{
    j = n.fenzi;
    k = n.fenmu;
}
if(k<j)
{
    temp = k;
    k = j;
    j = temp;
}
while(j!=0)				//辗转相除法?
{
    temp = k % j;
    k = j;
    j = temp;
}
n.fenzi = (n.fenzi) / k;
n.fenmu = (n.fenmu) / k;
return n;	//返回化简后的有理式(返回的与函数的类型一致)
}

struct rationalNumber rnAdd(struct rationalNumber x, struct rationalNumber y)
{
    struct rationalNumber k;
        k.fenzi = (x.fenzi) * (y.fenmu) + (x.fenmu) *(y.fenzi);  //这两行是分数加法同用的情况
        k.fenmu = (x.fenmu) * (y.fenmu);
        k = reduction(k);
    return k;
}

struct rationalNumber rnMean(struct rationalNumber a[], int n)
{
    int i;
    struct rationalNumber k;
    for(i=0;i<n-1;i++)
    {   
        a[i+1] = rnAdd(a[i],a[i+1]); //进行一个数据的叠加,此时已经化简好了。
    }
    a[i].fenmu = (a[i].fenmu) * n;

    return a[i];
}

/********** End **********/

书籍排序

任务描述

本关任务:编写一个完整的程序,对输入的n(n<100)本书进行排序并输出。

编程要求

在右侧编辑器中的Begin-End之间补充代码,编写一个完整的程序,对输入的n(n<100)本书进行排序并输出,具体要求如下:

程序输入
程序首先输入一个正整数n,然后输入n本书的信息,每本书用两行输入,第一行输入书的名称(书名最长不超过50个字符,中间可能有空格),第二行输入价格,价格为浮点数。

例如:

3
Programming in C
21.5
数据结构与算法
18.5
三体(一)
28.0

程序输出
程序要求对n本书按价格从低到高排序,如果价格相同,则按书名(字符串)字典序从小到大排序。最后输出排序结果,每行输出一本书的信息,首先输出价格,然后输出书名,中间用一个英文逗号和一个空格分开。

如上面输入对应的输出结果为:

18.5, 数据结构与算法
21.5, Programming in C
28, 三体(一)
#include <string.h>
#include <iostream>

using namespace std;

//请在此添加代码,实现书籍数据的输入、排序和输出
/********** Begin *********/
struct book{
	char name[55];
	float price;
}b[105];

int main()
{

	int n ;
    cin>>n;
	
	for(int i=0;i<n;i++)
	{
		getchar(); 
		cin.getline(b[i].name,55);
		cin>>b[i].price;		
	
	}
	// 排序
    for(int i=0;i<n-1;i++){
    	int k=i; //标记最小位置
		for(int j=i+1;j<n;j++){
			if(b[k].price > b[j].price)
			 k = j;
			else if(b[k].price == b[j].price && strcmp(b[k].name, b[j].name) > 0 ) //如果后面的串更小 
			 k = j; 
		}   
		if(k!=i){ //需要交换 
			book t = b[k];
			b[k] = b[i];
			b[i] = t;
			
		}
	} 

	for(int i=0; i<n; i++){
		cout << b[i].price << ", " << b[i].name << "\n";
	}
	return 0;
}

/********** End **********/


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值