目录
1.从键盘输入一个整数n(n≤10)和n个整数,存入数组a中,先依次输出各个数组元素的值,然后对数组a的所有元素值按从小到大的顺序重新排列后,再依次输出。
2.以下程序的功能是从键盘输入一个字符串,并删除该字符串中所有大写字母字符。
(1)将一个数组中的值逆序存储,然后按顺序输出数组。如原来数组存储为1、2、3、4、5,按要求重新存为5、4、3、2、1。
(6)将字符数组中的字母变换为其字母表顺序两位后的字母,'a'→'c','b'→'d',如果是‘z'或'Z',则分别变为'b'或'B',非字母字符不变。
(8)输入一行字符串,统计其中的单词个数,单词之间用空格隔开。如输入“I am a boy!”其中的单词个数为4个(不考虑大写字母)。
(9)已有一个排序好的数组(从小到大排序),现输入一个数,要求按原来排序的规律将它插入数组中。
前言
本篇基于《C语言程序设计学习指导与实践教程》(刘涛 叶明全 主编 | 上海交通大学出版社)教材书中的2.6.5章节 实验 数组的定义及使用。分析了实验中的相关例题,并编写了相关程序代码(部分代码源于网络),供大家参考。如果有更好的方法也希望各位伙伴留言讨论,文中若出现bug也请各位指出,谢谢!
本专栏其余几篇:2.5.5 实验1 循环结构程序设计https://blog.csdn.net/qq_45580875/article/details/134667276
2.7.5 函数程序设计https://blog.csdn.net/qq_45580875/article/details/134218701
实验目的
(1)熟练掌握一维数组和二维数组的定义、赋值和输入/输出的方法。
(2)熟练掌握字符数组和字符串函数的使用。
(3)掌握与数组有关的算法,进行综合程序设计。
实验内容与要求
1. 从键盘输入一个整数n(n≤10)和n个整数,存入数组a中,先依次输出各个数组元素的值,然后对数组a的所有元素值按从小到大的顺序重新排列后,再依次输出。
#include<stdio.h>
void main()
{
int a[10],n,i,j,min,t;
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%d",________);
for(i=0;i<n;i++)
printf("%d",________);
printf("\n");
for(i=0;i<_____;i++)
{
min=i;
for(j=_____;j<n;j++)
if(a[j]<a[min])
________;
t=_______;
a[i]=a[min];
_______=t;
}
for(i=0;i<n;i++)
printf("%d",a[i]);
printf("\n");
}
(1)输入、完善并调试上述源程序,直到没有错误为止。
(2)对源程序进行编译、连接和运行。
(3)写出程序的运行结果(输入3组数据)。
【完整程序】
#include<stdio.h>
void main()
{
int a[10], n, i, j, min, t;
scanf("%d", &n);
for (i = 0; i < n; i++)
scanf("%d", &a[i]); //通过循环给数组a连续赋值
for (i = 0; i < n; i++)
printf("%d ", a[i]); //将数组a中每个元素依次输出
printf("\n");
for (i = 0; i < 10; i++)
{
min = i;
for (j = i + 1; j < n; j++) //j=i+1表示从a[i]后一项开始
if (a[j] < a[min])
min = j;
t = a[i]; //此处为实现a[i]与a[min]的交换
a[i] = a[min];
a[min] = t;
}
for (i = 0; i < n; i++)
printf("%d ", a[i]);
printf("\n");
}
【运行结果】
注:这里输入时先输入n的值(即要输入多少个数),然后回车,再输入这n个值,用空格隔开。
输入整数n | 输入n个整数 | 输出(排列前) | 输出(排列后) |
5 | 5 4 2 3 1 | 5 4 2 3 1 | 1 2 3 4 5 |
8 | 1 3 5 7 2 4 6 8 | 1 3 5 7 2 4 6 8 | 1 2 3 4 5 6 7 8 |
10 | 1 6 8 7 4 5 2 0 9 3 | 1 6 8 7 4 5 2 0 9 3 | 0 1 2 3 4 5 6 7 8 9 |
2. 以下程序的功能是从键盘输入一个字符串,并删除该字符串中所有大写字母字符。
#include<stdio.h>
#include<string.h>
void main()
{
int n = 0, i;
char s[81];
gets(s);
i = 1; /*有错误*/
while (s[i] = '\0') /*有错误*/
{
if (!(s[i] >= 'A' && s[i] <= 'Z'))
s[n++] = s[i];
i++;
}
s[i] = '\0'; /*有错误*/
puts(s);
}
(1)输入并改正源程序中的错误。
(2)对源程序进行编译、连接和运行。
(3)写出程序运行结果。
【正确程序】
#include<stdio.h>
#include<string.h>
void main()
{
int n = 0, i;
char s[81];
gets(s);
i = 0; //将i=1改为i=0
while (s[i] != '\0') //将=改为!=
{
if (!(s[i] >= 'A' && s[i] <= 'Z'))
s[n++] = s[i];
i++;
}
s[n] = '\0'; //将s[i]改为s[n]
puts(s);
}
【结果演示】
3. 编程并上机调试运行。
(1)将一个数组中的值逆序存储,然后按顺序输出数组。如原来数组存储为1、2、3、4、5,按要求重新存为5、4、3、2、1。
①问题分析:
首先对于一个数组,我们要先定义其长度,在C语言中是不可以对数组的大小作动态定义的。通俗来说,就是先定义一个变量,用i接受后,再使用scanf("%d",&i)来从键盘上给i赋值,再定义一个数组a[i],这个方法是错误的。
对于这个问题,我们需要定义一个长度为5的数组,这里使用的是宏来声明长度N为5(方便后续调用)。
此题最核心的思想在于将首尾等间距的数进行交换,即第一个数与最后一个数交换,第二个数与倒数第二个数交换,以此类推。我们熟知,要实现两个数交换,就要首先设置定义一个中间变量(temp),来充当临时交换的容器。交换核心体现在示例代码第三个for循环结构中。
②代码示例:
#include <stdio.h>
#define N 5
void main() {
int temp, i; //temp为中间变量,i为循环变量,n为总元素个数
int a[N];
for (i = 0; i < N; i++)
scanf("%d", &a[i]); //连续给数组a添加元素
for (i = 0; i < N; i++)
printf("%d ", a[i]); //先将数组a中的元素依次输出
printf("\n");
for (i = 0; i < (N/2+1); i++) {
temp = a[i];
a[i] = a[N - i -1];
a[N - i -1] = temp;
}
//此行上方的for循环是通过一个中间变量temp来实现数组a中第i个元素和第N-i-1个元素对调
//当i=0时,实现第1个元素a[0]与第5个元素a[4]交换
for(i=0;i<N;i++)
printf("%d ", a[i]);
}
③代码演示:
注:这里每个数用空格隔开,最后一个数末尾为一个回车。
(2)打印出以下杨辉三角形(要求打印8行)。
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
……
①问题分析:
首先,找出杨辉三角形的规律。第一列及对角线元素均为1,且其它元素为其所在位置的上一行对应列和上一行前一列元素之和,如10= 4+6。这里我们通过定义一个二维数组a[N][N]来表示(N宏定义为8),10=4+6即可表示为a[5][2]=a[4][1]+a[4][2]。
找出一般规律后,写出一般式:a[i][j]=a[i-1][j-1]+a[i-1][j],这里i表示行,j表示列。接下来就用代码实现打印杨辉三角形前8行。
②代码示例:
#include <stdio.h>
#define N 8
void main() {
int i,j,a[N][N]; //i表示行,j表示列
for (i = 0; i < N; i++) {
//将数组a的第一列和对角线元素全部赋值为1
a[i][0] = 1;
a[i][i] = 1;
}
for (i = 2; i < N; i++) //第一行只有一个元素1,故从第二行开始
for (j = 1; j <= i - 1; j++)
a[i][j] = a[i - 1][j - 1] + a[i - 1][j];
//带入一般式
for (i = 0; i < N; i++) {
for (j = 0; j <= i; j++)
printf("%-5d",a[i][j]); //对其设置为左对齐5个单位
printf("\n");
}
/*通过for循环输出杨辉三角形,形状由内循环条件j <= i控制,
即列数不超过行数时,输出数在同一行,超过则进行换行*/
}
③代码演示:
(3)求4*4方阵两对角线元素之和及其转置矩阵。
①问题分析:
首先要定义一个4*4的二维数组,然后依次进行赋值,完成此方阵的完全初始化,再进行相关操作。
第一个问题是求此方阵的两对角线元素之和。这里要明确两对角线指的是主对角线和次对角线,而且因为是4*4的方阵,故不存在有重复的元素,故可以直接累加。主对角线的元素可以表示为a[i][i](行列相等),次对角线的元素可以表示成(a[i][3-i])。通过for循环分别累加主对角线元素与次对角线的元素后求和。
第二个问题是将此方阵转置。所谓转置就是将行列交换,用数组的方式表示就是b[i][j]=a[j][i],尽管这也是个交换元素的问题,但我们只需要在循环中输出时,将i与j的位置对调即可,不需要用到中间变量来交换。
②代码示例:
#include <stdio.h>
#define N 4
void main() {
int i,j,a[N][N]; //i表示行,j表示列
printf("输入一个4*4的方阵:\n");
//通过for循环嵌套来创建一个二维数组
for (i = 0; i < N; i++)
for (j = 0; j < N; j++)
scanf("%d", &a[i][j]);
//将输入的矩阵输出
for (i = 0; i < N; i++){
for (j = 0; j < N; j++)
printf("a[%d][%d]=%d\t", i, j, a[i][j]);
printf("\n");
}
/*实现求两对角线元素和*/
int sum = 0, sum1 = 0, sum2 = 0;
//sum1为主对角线元素和,sum2为次对角线元素和,sum为总和
for (i = 0; i < N; i++)
sum1 += a[i][i]; //求出主对角线元素和
for (i = 0; i < N; i++)
sum2 += a[i][3 - i];//求出次对角线元素和
sum = sum1 + sum2; //求出总和
printf("sum=%d\n", sum); //输出总和结果
/*实现方阵转置*/
//输出转置后的方阵
for (i = 0; i < N; i++) {
for (j = 0; j < N; j++)
printf("a[%d][%d]=%d\t", i, j, a[j][i]); //将i,j的位置对调
printf("\n");
}
}
③代码演示:
*注:输入矩阵时,可以通过回车换行,每个数字间用空格隔开。
(4)输出Fibonacci数列的前二十项
①问题分析:
此题要求输出斐波那契数列的前二十项,我们熟知此数列的特点为,第一项和第二项为1,从第三项开始,每项为前两的和。故可以创建一个一维数组f[20],定义f[0]=f[1]=1;然后通过循环交替的方式来进行依次赋值,即循环体为f[i]=f[i-1]+f[i-2],求出剩下的18项。
②代码示例:
#include <stdio.h>
#define N 20
void main() {
int f[N], i;
f[0] = 1; f[1] = 1;
for (i = 2; i < N; i++)
f[i] = f[i - 1] + f[i - 2]; //第三项其后项等于前两项和
for (i = 0; i < N; i++)
printf("%d ", f[i]); //循环遍历输出斐波那契数列
}
③代码演示:
(5)按下列公式计算,求A20的值。
A1=1,A2=1;
A3=3*A1+A2;
A4=3*A2+A3;
……
①问题分析:
可以看出此题与第四题中的斐波那契数列极为相似,只是从第三项开始,每项等于前两项中,第一项的三倍加上第二项。仿照斐波那契数列即可。通式改为a[i]=a[i-2]*3+a[i-1]。
② 代码示例:
#include <stdio.h>
#define N 20
void main() {
int A[N], i;
A[0] = 1; A[1] = 1;
for (i = 2; i < N; i++)
A[i] = 3 * A[i - 2] + A[i - 1]; //第三项其后项等于前两项中第一项三倍与第二项和
printf("A20的值为:%d\n ", A[19]); //输出A[19],即第20项
}
③代码演示:
(6)将字符数组中的字母变换为其字母表顺序两位后的字母,'a'→'c','b'→'d',如果是‘z'或'Z',则分别变为'b'或'B',非字母字符不变。
①问题分析:
此题的背景为恺撒密码,即通过将明文中的所有字母都在字母表上向后(或向前)按照一个固定数目进行偏移后被替换成密文。
这里是将所有的字母向后偏移两个单位,非字母单位不做处理。此处还需熟悉字母表所对应的ASCII码值。a~z对应的ASCII码值为97~122,A~Z对应的ASCII码值为65~90。
要注意,这里要考虑到最后两个字母偏移后是跳回到前两个字母,故要加以判断条件。通过for循环嵌套if判断语句即可实现。
②代码示例:
#include <stdio.h>
#include<string.h>
void main() {
char str[100];
gets(str);
printf("转换前的字符数组:%s\n", str);
int len = strlen(str);
for (int i = 0; i < len; i++) {
if ((str[i] >= 65 && str[i] <= 90) || (str[i] >= 97 && str[i] <= 122))
//第一个if用来判断数组中的元素是否为字母
if (str[i] == 89 || str[i] == 90 || str[i] == 121 || str[i] == 122)
//判断字母是否为最后两个字母
str[i] += 2 - 26;
//后推两位再从首位计算
else
str[i] += 2;
//后推两位
}
printf("转换后的字符数组:%s\n", str);
}
③代码演示:
(7)将两个字符串连接起来,不要用strcat函数。
①问题分析:
这里要实现连接两个字符串,而不使用字符串连接函数。考虑字符串连接的含义,即将第二个字符串的首位接在第一个字符串末尾,当然第一个字符串数组要足够大,否则会出现溢出。
先用循环找出第一个字符串结尾的位置,及判断str1的结束标识符'\0'出现在哪,用一个变量i来记录,再将另一个字符串从头开始,利用循环依次将元素添加到str1结尾后。
②代码示例:
#include <stdio.h>
#include<string.h>
int main() {
char str1[50], str2[50];
int i, j;
gets(str1);
gets(str2);
// 找到str1的末尾
i = 0;
while (str1[i] != '\0') {
i++;
}
// 将str2复制到str1的末尾
j = 0;
while (str2[j] != '\0') {
str1[i] = str2[j];
i++;
j++;
}
str1[i] = '\0';
printf("复制后的结果为:%s\n", str1);
return 0;
}
③代码演示:
(8)输入一行字符串,统计其中的单词个数,单词之间用空格隔开。如输入“I am a boy!”其中的单词个数为4个(不考虑大写字母)。
①问题分析:
计算一串字符串中的单词个数,我们可以通过计算其中的空格来实现。示例中的短句有4个单词,包含了3个空格,那么我们可以通过设置一个计数器,计算出空格数量后再加一就可以得出单词的个数。
②代码示例:
#include <stdio.h>
#include<string.h>
void main() {
char str[100];
int count = 0;
printf("请输入一行英文字符串:\n");
gets(str);
int len = strlen(str);
for (int i = 0; i < len; i++) {
if (str[i] == ' ')
count++;
}
printf("该字符串中有%d个单词\n", count + 1);
}
③代码演示:
(9)已有一个排序好的数组(从小到大排序),现输入一个数,要求按原来排序的规律将它插入数组中。
①问题分析:
这里要提前预设好一个按从小到大排序的数组,再插入一个数而保持原来的排序规律。我们可以依次将数组中的的元素遍历,与新的元素进行比较。
新元素从数组中的最后一位元素开始比较,如果新元素小,则将被比较的元素在数组中的位置后移一位,依次进行,直至找到一个位置使得左边元素比该元素小,右边元素比该元素大。找出该位置后,再将此元素插入到该位置。最终输出重新排序好的数列。
②代码示例:
#include <stdio.h>
void main() {
int num, i = 0;
int a[9] = { 0,1,2,3,5,6,8,9 };
//定义一个按从小到大排序的数组
//在0~9中抽取了4,7两元素,下面以这两个元素作演示
for (i = 0; i < 9; i++)
printf("%d ", a[i]);
printf("\n");
int last = 7;
//定义last为上面所定义的数组a的最末端元素位置
//因为数组a有8个元素所以最后一个数的位置为7
scanf("%d", &num);
//获取num的值,这里我们用4,7为例
//先输出数组a
while (last >= 0 && num < a[last]) {
a[last + 1] = a[last];
//如果这个num比右边元素小,则让右边的元素再向右移一位
//大于右边元素则跳出循环
last--;
//last自减1,即表示与前一个元素比较
}
a[last + 1] = num;
//将num放在找到的位置
for (i = 0; i < 9; i++)
printf("%d ", a[i]);
//输出重新排列的数组
printf("\n");
}
③代码演示:
(10)编写程序实现从字符数组s中删除存放在c中的字符。
①问题分析:
先定义一个字符数组,再定义一个字符变量c,分别给其赋值。赋值后,用一个循环判断结构来遍历出字符数组中的各个元素,与要删除的元素c进行判断。若相等则将此元素在s中后的元素替代该元素的位置(即将该元素进行覆盖,后面的元素前移)。
②代码示例:
#include <stdio.h>
#include<string.h>
int main() {
char s[50], c;
//定义一个字符数组,和一个字符型变量
int i, j;
//定义循环变量
gets(s);
//给字符数组s赋值一个字符串
scanf("%c", &c);
//给c赋值一个字符
for (i = 0, j = 0; s[i] != '\0'; i++)
if (s[i] != c)
s[j++] = s[i];
//此循环判断结构是为找出字符串s中是否与c相同的元素
//若不相同,则将s[i]赋值给s[j++],这里相当于对同一个字符串进行重新赋值
//若相同,则不进行赋值,直接跳转判断下一个元素
s[j] = '\0';
//最后给字符串末尾添加结束标识符 \0
puts(s);
//输出处理后的字符串s
return 0;
}
③代码演示:
这里用原先的编译器一直运行不出想要的结果,换了一个编译器就解决了!!!: (
思考题
1. 在实验中,如果将第1题的题目改为:从键盘输入一个整数n(n≤10)和n个整数,存入数组a中,先依次输出各个数组元素的值,然后对数组a的所有元素值按从小到大的顺序重新排列后,再依次输出,要求采用冒泡法进行排序,程序应该如何修改?如果在原程序的运行结果上,需要对输出结果进行控制,每行输出5个数据,程序要如何修改?
2. 在第2题中,如果将“gets(s);”改为“scanf("%s",s);”对程序运行有无影响?并说明其原因。如果将if(!(s[i]>='A'&&s[i]<='Z')) 中的条件改为 if(s[i]<'A' || s[i]>'Z'),对程序的结果有无影响?为什么?
【解析】
1.
(1)第一问:将程序用冒泡排序进行改写。这个也是十分熟悉了,甚至说比例题中的理解起来更加容易(我在第一问中那个for循环里的if结构中卡了半天,而冒泡排序就用不着那条要补充的语句,更不用多定义一个变量min),废话少说,直接上代码。
代码示例:
#include<stdio.h>
void main()
{
int a[10], n, i, j, t;
scanf_s("%d", &n);
for (i = 0; i < n; i++)
scanf("%d", &a[i]);
for (i = 0; i < n; i++)
printf("%d ", a[i]);
printf("\n");
for (i = 0; i < 10; i++){
for (j = 0; j < n - i - 1; j++)
if (a[j] > a[j + 1]) {
//用中间变量t实现a[j]和a[j+1]两相邻元素的交换
t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
}
}
for (i = 0; i < n; i++)
printf("%d ", a[i]);
printf("\n");
}
(2)第二问:在原程序的输出结果中实现每行输出五个元素。这里只需要加一个判断条件,当条件成立时输出一个换行符即可,即将最后一个for循环输出结构内加一层if结构。
即:
for (i = 0; i < n; i++) {
printf("%d ", a[i]);
if ((i + 1) % 5 == 0)
printf("\n");
}
2.
(1)第一问:将gets(s);改成scanf("%s",s);。改后影响在于当输入的字符串中包含空格会直接结束对字符串的赋值(程序会将空格当成结束标志),从而导致程序输出的结果不一定符合预期。
(2)第二问:将if(!(s[i]>='A'&&s[i]<='Z'))中的条件改为 if(s[i]<'A' || s[i]>'Z')。从数学的角度来看,对(p且q)的否定,与(非p)或(非q)是等价的。原始的条件 if(!(s[i]>='A'&&s[i]<='Z')) 检查字符是否不是大写字母,而修改后的条件 if(s[i]<'A' || s[i]>'Z') 同样检查字符是否为大写字母。因此,对这个条件进行更改后并不会对程序输出结果产生影响。
总结
上面一些题目都是有关于数组的定义与使用,更重要的是通过for循环或while循环和if判断,来对数组中的元素进行相关的处理操作,并接触了本阶段的第一个排序算法——冒泡排序。且本篇所涉及到的题目都是一维数组,难度适中。要明白数组的下标是从0开始的,上限是数组长度减1;字符串是以'\0'为结束标志的特殊字符型数组,结束标志不计入字符串长度,但存放字符串的字符数组的长度必须比字符串中字符的个数多1等。
总之,要更加熟练掌握数组的运用就需要多练多思考。
到这里就结束了,希望各位同学阅读后能有所收获,也欢迎各位积极讨论交流!