由于不可抗力,我们获得了三天假……噢,不对,我们需要上三天线上课。
这种的课程上我们学习了数组,这是一种很方便的数据类型。实际使用才能感受到,那么我们就直接开始看今天的作业吧!
目录
上机实验
6-1 求10个数平均值
本题要求编写程序, 求给定的10个数中的平均值(下标从0开始)。
输入格式:
在一行输入10个整数,用空格分开。
输出格式:
在一行中输出平均值,保留2位小数。
输入样例:
7 2 8 5 10 1 9 3 6 4
输出样例:
5.50
这一题的重点在于循环输入,如果不用数组,那我们已经写过远比这困难得多的程序。
这道题本身是不是也写过
这是不用数组的写法:
#include <stdio.h>
int main()
{
int sum = 0, num = 10, n;
for (int i = 0; i < num; i++)
{
scanf("%d", &n);
sum += n;
}
printf("%.2f", (float)sum / num);
return 0;
}
非常的简单,那我们再来看看用数组通常会怎么写:
#include <stdio.h>
#define NUM 10
int main()
{
int arrNum[NUM], sum = 0;
for (int i = 0; i < NUM; i++)
{
scanf("%d", &arrNum[i]);
}
for (int i = 0; i < NUM; i++)
{
sum += arrNum[i];
}
printf("%.2f", (float)sum / NUM);
return 0;
}
这里可以把第二个循环放入第一个循环内,但是这样一来就完全没有用数组的必要了。
6-2 求超过平均值的个数
本题要求编写程序,求给定的n个数中超过(含相等)平均值的个数(下标从0开始)。
输入格式:
输入在第一行中给出一个正整数n(1<n≤10)。第二行输入n个整数,用空格分开。
输出格式:
在一行中输出平均值,保留2位小数,超过平均值的数的个数,用一个空格分开
输入样例:
5
2 4 1 6 7
输出样例:
4.00 3
这一题是有用数组的必要的。如果不用数组,我们将很难在获得平均值之后再返回寻找大于平均值的数。
数组输入每次都重新写太麻烦了,有没有办法写成一个函数,传递的参数是数组呢?这是有的,不过传递的参数不是数组,而是数组的首指针(头指针)。由于数组在内存内是一块连续的空间,所以只需要开始的位置和大小就能找到整个数组。
这里给出一种实现思路:
/**
* @brief 数组输入
*
* @param arr 数组头指针
* @param num 数组长度
* @return void
*/
void ArrInput(int *arr, int num)
{
for (int i = 0; i < num; i++)
scanf("%d", &arr[i]);
}
同样的,我们也可以把数组求平均值写成一个函数,方便调用。这一题的代码并不复杂,很简单就写出来了:
#include <stdio.h>
#define NUM_MAX 16
void ArrInput(int *arr, int num);
float ArrAverage(int *arr, int num);
int main()
{
int arrNum[NUM_MAX], num, count = 0;
float ave;
scanf("%d", &num);
ArrInput(arrNum, num);
ave = ArrAverage(arrNum, num);
for (int i = 0; i < num; i++)
{
if(arrNum[i] >= ave)
count++;
}
printf("%.2f %d", ave, count);
return 0;
}
/**
* @brief 数组输入
*
* @param arr 数组头指针
* @param num 数组长度
* @return void
*/
void ArrInput(int *arr, int num)
{
for (int i = 0; i < num; i++)
scanf("%d", &arr[i]);
}
/**
* @brief 求数组的平均值
*
* @param arr 数组头指针
* @param num 数组长度
* @return float
*/
float ArrAverage(int *arr, int num)
{
int sum = 0;
for (int i = 0; i < num; i++)
sum += arr[i];
return (float)sum / num;
}
6-3 求最大值及其下标
本题要求编写程序,找出给定的n个数中的最大值及其对应的最小下标(下标从0开始)。
输入格式:
输入在第一行中给出一个正整数n(1<n≤10)。第二行输入n个整数,用空格分开。
输出格式:
在一行中输出最大值及最大值的最小下标,中间用一个空格分开。(以第一次出现为准)
输入样例:
6
2 8 10 1 9 10
输出样例:
10 2
这么快数组输入函数就能复用了,真棒!
找出最大值并不复杂,但这是选择排序的前奏,熟悉一下总是好的。代码如下:
#include <stdio.h>
#define NUM_MAX 16
void ArrInput(int *arr, int num);
int main()
{
int arrNum[NUM_MAX], num;
int maxVal = 0, maxNum;
float ave;
scanf("%d", &num);
ArrInput(arrNum, num);
for(int i = 0; i < num; i++)
{
if(arrNum[i] > maxVal)
{
maxVal = arrNum[i];
maxNum = i;
}
}
printf("%d %d", maxVal, maxNum);
return 0;
}
/**
* @brief 数组输入
*
* @param arr 数组头指针
* @param num 数组长度
* @return void
*/
void ArrInput(int *arr, int num)
{
for (int i = 0; i < num; i++)
scanf("%d", &arr[i]);
}
6-4 交换最小值和最大值
本题要求编写程序,先将输入的一系列整数中的最小值与第一个数交换,然后将最大值与最后一个数交换,最后输出交换后的序列。
题目保证最大和最小值都是唯一的。
输入格式:
输入在第一行中给出一个正整数n(1<n≤10),第二行给出n个整数,数字间以空格分隔。
输出格式:
在一行中顺序输出交换后的序列,每个整数后跟一个空格。
输入样例:
5
8 2 5 1 4
输出样例:
1 2 5 4 8
想不到作业居然想一步步引导我们去写排序。这个程序需要做的事情有点多,咱写成函数可读性会高很多。
代码如下:
#include <stdio.h>
#define NUM_MAX 16
void ArrInput(int *arr, int num);
void ArrPrint(int *arr, int num);
void ArrSwap(int *arr, int a, int b);
void ArrFindMaxMin(int *arr, int num, int *val);
int main()
{
int arrNum[NUM_MAX], num;
int maxMin[2] = {0};
scanf("%d", &num);
ArrInput(arrNum, num);
ArrFindMaxMin(arrNum, num, maxMin);
ArrSwap(arrNum, num - 1, maxMin[0]);
ArrSwap(arrNum, 0, maxMin[1]);
ArrPrint(arrNum, num);
return 0;
}
/**
* @brief 数组输入
*
* @param arr 数组头指针
* @param num 数组长度
* @return void
*/
void ArrInput(int *arr, int num)
{
for (int i = 0; i < num; i++)
scanf("%d", &arr[i]);
}
/**
* @brief 数组输输出
*
* @param arr 数组头指针
* @param num 数组长度
* @return void
*/
void ArrPrint(int *arr, int num)
{
for (int i = 0; i < num; i++)
printf("%d ", arr[i]);
}
/**
* @brief 交换数组内的两个值
*
* @param arr 数组头指针
* @param a 下标1
* @param b
*/
void ArrSwap(int *arr, int a, int b)
{
int x;
x = arr[a];
arr[a] = arr[b];
arr[b] = x;
}
/**
* @brief 找到数组内最大和最小的下标
*
* @param arr 数组头指针
* @param num 数组长度
* @param val 存放最大值和最小值的“下标”的数组
*/
void ArrFindMaxMin(int *arr, int num, int *val)
{
int MM[2] = {0, 10};
for (int i = 0; i < num; i++)
{
if(arr[i] > MM[0])
{
MM[0] = arr[i];
val[0] = i;
}
if(arr[i] < MM[1])
{
MM[1] = arr[i];
val[1] = i;
}
}
}
6-5 输出学生成绩
本题要求编写程序,根据输入学生的成绩,统计并输出学生的平均成绩、最高成绩和最低成绩。
输入格式:
输入第一行首先给出一个正整数n(1<n≤10),表示学生的个数。第一行给出n个学生的成绩,数字间以空格分隔。
输出格式:
按照以下格式输出:平均分保留2位小数。
average = 平均成绩max = 最高成绩min = 最低成绩
结果均保留两位小数。
输入样例:
3
85 90 95
输出样例:
average = 90.00 max = 95 min = 85
找最大、最小、平均的函数咱都写过了,直接用吧!
代码如下:
#include <stdio.h>
#define NUM_MAX 16
void ArrInput(int *arr, int num);
void ArrFindMaxMin(int *arr, int num, int *val);
float ArrAverage(int *arr, int num);
int main()
{
int arrNum[NUM_MAX], num;
int maxMin[2] = {0};
scanf("%d", &num);
ArrInput(arrNum, num);
ArrFindMaxMin(arrNum, num, maxMin);
printf("average = %.2f max = %d min = %d", ArrAverage(arrNum, num), arrNum[maxMin[0]], arrNum[maxMin[1]]);
return 0;
}
/**
* @brief 数组输入
*
* @param arr 数组头指针
* @param num 数组长度
* @return void
*/
void ArrInput(int *arr, int num)
{
for (int i = 0; i < num; i++)
scanf("%d", &arr[i]);
}
/**
* @brief 找到数组内最大和最小的下标
*
* @param arr 数组头指针
* @param num 数组长度
* @param val 存放最大值和最小值的“下标”的数组
*/
void ArrFindMaxMin(int *arr, int num, int *val)
{
int MM[2] = {0, 10};
for (int i = 0; i < num; i++)
{
if(arr[i] > MM[0])
{
MM[0] = arr[i];
val[0] = i;
}
if(arr[i] < MM[1])
{
MM[1] = arr[i];
val[1] = i;
}
}
}
/**
* @brief 求数组的平均值
*
* @param arr 数组头指针
* @param num 数组长度
* @return float
*/
float ArrAverage(int *arr, int num)
{
int sum = 0;
for (int i = 0; i < num; i++)
sum += arr[i];
return (float)sum / num;
}
6-6 数组中的数逆序存放
本题要求编写程序,将给定的n个整数存入数组中,将数组中的这n个数逆序存放,再按顺序输出数组中的元素。
输入格式:
输入在第一行中给出一个正整数n(1≤n≤10)。第二行输入n个整数,用空格分开。
输出格式:
在一行中输出这n个整数的处理结果,相邻数字中间用一个空格分开,行末不得有多余空格。
输入样例:
4
10 8 1 2
输出样例:
2 1 8 10
这一题不算复杂,可以先复制一个一样的数组,然后反向给原数组赋值即可。代码如下:
#include <stdio.h>
#define NUM_MAX 16
void ArrInput(int *arr, int num);
void ArrReverse(int *arr, int num);
void ArrPrint(int *arr, int num);
void ArrCopy(int *arr, int *arr1);
int main()
{
int arrNum[NUM_MAX], num;
scanf("%d", &num);
ArrInput(arrNum, num);
ArrReverse(arrNum, num);
ArrPrint(arrNum, num);
return 0;
}
/**
* @brief 数组输入
*
* @param arr 数组头指针
* @param num 数组长度
* @return void
*/
void ArrInput(int *arr, int num)
{
for (int i = 0; i < num; i++)
scanf("%d", &arr[i]);
}
/**
* @brief 数组输出
*
* @param arr 数组头指针
* @param num 数组长度
* @return void
*/
void ArrPrint(int *arr, int num)
{
for (int i = 0; i < num; i++)
printf("%d ", arr[i]);
}
/**
* @brief 数组反向
*
* @param arr 数组头指针
* @param num 数组长度
*/
void ArrReverse(int *arr, int num)
{
int A[NUM_MAX];
ArrCopy(arr, A);
for (int i = 0; i < num; i++)
{
arr[i] = A[num - 1 - i];
}
}
/**
* @brief 复制数组
*
* @param arr 被复制数组头指针
* @param arr1 目标数组头指针
*/
void ArrCopy(int *arr, int *arr1)
{
int n = NUM_MAX;
for (int i = 0; i < n; i++)
{
arr1[i] = arr[i];
}
}
6-7 查找整数
本题要求编写程序,从输入的n个整数中查找给定的X。如果找到,输出X的位置(从0开始数);如果没有找到,输出“Not Found”。
输入格式:
输入在第一行中给出两个正整数n(1≤n≤20)和X,第二行给出n个整数。数字均不超过长整型,其间以空格分隔。
输出格式:
在一行中输出X的位置,或者“Not Found”。
输入样例1:
5 7
3 5 7 1 9
输出样例1:
2
输入样例2:
5 7
3 5 8 1 9
输出样例2:
Not Found
非常简单的遍历,直接看代码吧:
#include <stdio.h>
#define NUM_MAX 16
void ArrInput(int *arr, int num);
int ArrFind(int *arr, int num, int find);
int main()
{
int arrNum[NUM_MAX], num;
int findNum, found;
scanf("%d%d", &num, &findNum);
ArrInput(arrNum, num);
found = ArrFind(arrNum, num, findNum);
if(found == -1)
{
printf("Not Found");
}
else
{
printf("%d", found);
}
return 0;
}
/**
* @brief 数组输入
*
* @param arr 数组头指针
* @param num 数组长度
* @return void
*/
void ArrInput(int *arr, int num)
{
for (int i = 0; i < num; i++)
scanf("%d", &arr[i]);
}
/**
* @brief 寻找数组内是否有对应的数
*
* @param arr 数组头指针
* @param num 数组长度
* @param find 要找的数
* @return int 找到返回对应下标,未找到返回-1
*/
int ArrFind(int *arr, int num, int find)
{
for (int i = 0; i < num; i++)
{
if(arr[i] == find)
return i;
}
return -1;
}
6-8 输出数组元素
本题要求编写程序,对顺序读入的n个整数,顺次计算后项减前项之差,并按每行三个元素的格式输出结果。
输入格式:
输入的第一行给出正整数n(1<n≤10)。随后一行给出n个整数,其间以空格分隔。
输出格式:
顺次计算后项减前项之差,并按每行三个元素的格式输出结果。数字间空一格,行末不得有多余空格。
输入样例:
10
5 1 7 14 6 36 4 28 50 100
输出样例:
-4 6 7
-8 30 -32
24 22 50
不是很喜欢这种题……
不过题目不难,直接就能写出来。
#include <stdio.h>
#define NUM_MAX 16
void ArrInput(int *arr, int num);
int main()
{
int arrNum[NUM_MAX], num;
scanf("%d", &num);
ArrInput(arrNum, num);
for (int i = 0; i < num - 1; i++)
{
printf("%d\t", arrNum[i + 1] - arrNum[i]);
if(!((i + 1) % 3))
printf("\n");
}
return 0;
}
/**
* @brief 数组输入
*
* @param arr 数组头指针
* @param num 数组长度
* @return void
*/
void ArrInput(int *arr, int num)
{
for (int i = 0; i < num; i++)
scanf("%d", &arr[i]);
}
6-9 数字加密
本题要求编写程序,输入一个四位数,将其加密后输出。方法是将该数每一位上的数字加9,然后除以10取余,做为该位上的新数字,最后将千位和十位上的数字互换,百位和个位上的数字互换,组成加密后的新四位数。例如输入1257,经过加9取余后得到新数字0146,再经过两次换位后得到4601。
输入格式:
输入在一行中给出一个四位的整数x,即要求被加密的数。
输出格式:
在一行中按照格式“The encrypted number is V”输出加密后得到的新数V。
输入样例:
1257
输出样例:
The encrypted number is 4601
我们之前曾写过,单独取出一位数的函数。现在我们要写将一个数转化为一个数组的函数了!
不过这一题是可以不用数组的,因为数字长度固定了,不需要一堆数据。但是为了较好的泛用性,在这里我依然用数组写。
#include <stdio.h>
#include <math.h>
#define NUM_MAX 16
void ArrInput(int *arr, int num);
void ArrSwap(int *arr, int a, int b);
int ArrNumToArr(int val, int *arr);
int ArrArrToNum(int *arr, int num);
int main()
{
int arrNum[NUM_MAX], num, val;
scanf("%d", &val);
num = ArrNumToArr(val, arrNum);
//---------------------------------加密部分
for (int i = 0; i < num; i++)
{
arrNum[i] = (arrNum[i] + 9) % 10;
}
ArrSwap(arrNum, 0, 2);
ArrSwap(arrNum, 1, 3);
//---------------------------------
printf("The encrypted number is %d", ArrArrToNum(arrNum, num));
return 0;
}
/**
* @brief 数组输入
*
* @param arr 数组头指针
* @param num 数组长度
* @return void
*/
void ArrInput(int *arr, int num)
{
for (int i = 0; i < num; i++)
scanf("%d", &arr[i]);
}
/**
* @brief 交换数组内的两个值
*
* @param arr 数组头指针
* @param a 下标1
* @param b
*/
void ArrSwap(int *arr, int a, int b)
{
int x;
x = arr[a];
arr[a] = arr[b];
arr[b] = x;
}
/**
* @brief 将数字转化成数组,遵循[0]个位[1]十位……即低位在前
*
* @param val 待转换的数字
* @param arr 目标数组
* @return int 数字位数
*/
int ArrNumToArr(int val, int *arr)
{
int i;
for (i = 0; val; i++)
{
arr[i] = val % 10;
val /= 10;
}
return i;
}
/**
* @brief 将数组转化成数字
*
* @param arr
* @param num
* @return int
*/
int ArrArrToNum(int *arr, int num)
{
int sum = 0;
for (int i = 0; i < num; i++)
{
sum += arr[i] * pow(10, i);
}
return sum;
}
这样写出来的就可以兼容任意位数,只需要更改加密部分就行。
6-10 查验身份证
一个合法的身份证号码由17位地区、日期编号和顺序编号加1位校验码组成。校验码的计算规则如下:
首先对前17位数字加权求和,权重分配为:{7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};然后将计算的和对11取模得到值Z;最后按照以下关系对应Z值与校验码M的值:
Z:012345678910
M:10 X 98765432
现在给定一些身份证号码,请你验证校验码的有效性,并输出有问题的号码。
输入格式:
输入在第一行中给出一个正整数n(1<n≤100)是输入的身份证号码的个数,随后n行,每行给出1个18位身份证号码。
输出格式:
按照输入的顺序每行输出1个有问题的身份证号码。这里并不检验前17位是否合理,只检查前17位是否全为数字且最后1位校验码计算准确。如果所有号码都正常,则输出All passed。
输入样例1:
4
320124198808240056
12010X198901011234
110108196711301866
37070419881216001X
输出样例1:
12010X198901011234
110108196711301866
37070419881216001X
输入样例2:
2
320124198808240056
110108196711301862
输出样例2:
All passed
这一题…真的可以不用字符串吗……?!!!
可是字符串咱还没学,如果没有X还是能用数组的,但是有X感觉…数组不好表示啊。
没事,字符串就字符串,直接理解字符串是特殊的数组就好了!
那咱来看看吧,该怎么写。
首先得到一串18位身份证号码的字符串,要先把它变成数组。
变成数组之后,需要加权求和取模,得到的结果再与校验码值一一对应。
嗯,还是有点复杂的,说实话这题放这里不但有点超前,甚至有点超纲…由于字符串数量不确定,所以必须要用字符串数组,但是这东西吧…比较复杂。在进行指针传递的时候,不是那么轻松。
我觉得这题不用深究,不过还是写了实现的思路,代码如下,只当参考:
#include <stdio.h>
#define NUM_MAX 16
#define STR_LEN 20
void StrInput(char *atr, int num);
void StrPrint(char *str, int num);
void NumCheck(char *str, int num);
void AndCheck(char *arr, char *str, int num);
void StrToArr(char *str, int num, char *arr);
char M[STR_LEN] = "10X98765432"; //校验码映射
char power[] = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}; //权重映射
int main()
{
//数组最后一位[19]放有效校验
char strIdnum[NUM_MAX][STR_LEN] = {0};
char arrIdnum[NUM_MAX][STR_LEN] = {0};
int num;
scanf("%d", &num);
StrInput(strIdnum[0], num);
NumCheck(strIdnum[0], num);
StrToArr(strIdnum[0], num, arrIdnum[0]);
AndCheck(arrIdnum[0], strIdnum[0], num);
StrPrint(strIdnum[0], num);
return 0;
}
/**
* @brief 字符串输入
*
* @param str 字符串数组首地址
* @param num 字符串数组长度
*/
void StrInput(char *str, int num)
{
for (int i = 0; i < num; i++)
{
scanf("%s", &str[i * STR_LEN]);
}
}
/**
* @brief 字符串打印
*
* @param str 字符串数组首地址
* @param num 字符串数组长度
*/
void StrPrint(char *str, int num)
{
int flag = 1;
for (int i = 0; i < num; i++)
{
if(str[i * STR_LEN + STR_LEN - 1] == -1)//校验不通过,含数字校验和和校验
{
printf(&str[i * STR_LEN]);
printf("\n");
flag = 0;
}
}
if (flag)
printf("All passed");
}
/**
* @brief 数字校验,校验是否能够都转化为数字
*
* @param str
* @param num
*/
void NumCheck(char *str, int num)
{
for (int i = 0; i < num; i++)
{
for (int j = 0; j < STR_LEN - 3; j++)
{
if (str[i * STR_LEN + j] < 48 || str[i * STR_LEN + j] > 57) //0和9的ASCII
{
str[i * STR_LEN + STR_LEN - 1] = -1;
break;
}
}
}
}
/**
* @brief 和校验,只计算数字校验通过的
*
* @param arr
* @param num
*/
void AndCheck(char *arr, char *str, int num)
{
int sum;
for (int i = 0; i < num; i++)
{
sum = 0;
if(arr[i * STR_LEN + STR_LEN - 1] == -1)//数校验不通过
continue;
for (int j = 0; j < STR_LEN - 3; j++)
{
sum += arr[i * STR_LEN + j] * power[j];
}
arr[i * STR_LEN + STR_LEN - 3] = sum % 11;
if(str[i * STR_LEN + STR_LEN - 3] != M[arr[i * STR_LEN + STR_LEN - 3]])//和校验不通过
str[i * STR_LEN + STR_LEN - 1] = -1;
}
}
/**
* @brief 把字符串数组变成二维数组方便后续计算
*
* @param str 字符串数组首地址
* @param num 字符串数组长度
* @param arr 二维数组首地址
*/
void StrToArr(char *str, int num, char *arr)
{
for (int i = 0; i < num; i++)
{
if(str[i * STR_LEN + STR_LEN - 1] == -1)//校验不通过
{
arr[i * STR_LEN + STR_LEN - 1] = -1;
continue;
}
for (int j = 0; j < STR_LEN - 3; j++)
{
arr[i * STR_LEN + j] = str[i * STR_LEN + j] - 48;
}
}
}
可以看到同样采用了大量函数和二维数组。为了增加一点可读性,加入了一些注释。即使这样依然较为晦涩……
结语
这周的作业并不都很复杂,除开最后一题,其它的都不是很难。
在这周的数组作业内,咱写了许许多多的数组方法(函数),以后可以直接调用,甚至可以写成一个简单的库!
补充:数组参数传递的其它写法
需要指出的是,数组作为函数的参数传递的时候,除了上面的传首地址外,还有另一种写法:
int Func(int arr[]);
这种写法也是可以的,并且传递的也是指针:
Func(arr);
这样就能完成数组的参数传递。注意,这种传递也是双向的,因为传入的也是地址。
下面是一个例子:
#include <stdio.h>
void Func(int arr[]);
int main()
{
int arr[10] = {0};
Func(arr);
printf("%d", arr[0]);
return 0;
}
void Func(int arr[])
{
arr[0] = 1;
}
在这个例子内,输出的值为1。即在Func函数内更改的arr[0]是能够在函数外部生效的。
另外,还有一种读指针内部数据的方法,直接计算偏移量即可。这会在以后指针节学习,在此不赘述了。
为什么不想写了?哥!这篇文章已经一万多字了啊!能看到这里的都是勇士!!