大一C语言挣扎之路6(数组)
一 数组的概念
- 实际应用中,经常会遇到需要处理大量同一性质数据的情况,为解决这种问题,C语言提供了数组(array)类型,用来批量处理同类型数据。
- 数组是一组数据类型相同的有序数据的集合。数组名是一个标识符,数组中所含的每个数据称为数组元素,它们具有相同的数据类型。
- 数组分为一维数组和多维数组。
/*使用一维数组存放不多于50个学生的成绩,并计算平均值。*/
#include <stdio.h>
int main()
{ /*定义数组用于存储学生的考试成绩,初始化为0*/
float score[50]={0};
int num; /*num从键盘读入,表示实际学生人数*/
float sum=0, average;/*定义变量存储成绩的总和和平均值*/
int i;
do
{
printf("Input the number of students:");
scanf("%d",&num) ; /*输入符合要求的实际学生人数*/
}while (num<=0||num>50);
printf("Input the score :");
for ( i=0;i<num;i++)
{
scanf("%f",&score[i]); /*逐个输入学生的成绩*/
sum+= score[i]; /*计算总分*/
}
average= sum/num; /*求平均分*/
printf("The average is :%5.2f\n", average);
return 0;
}
//运行此程序:
//Input the number of students:3<回车>
//Input the score :90 92 95<回车>
//The average is :92.33
二 一维数组
2.1.一维数组的定义
①一维数组的定义格式如下:
类型标识符 数组名[整型常量表达式];
int score[5];
int score[5]={0};
②类型标识符表示该数组中的数组元素的类型,各个元素的数据类型都是一致的;
数组名是由用户自己定义的合法标识符;
整型常量表达式定义了该数组中存储元素的最大容量,即数组长度。
③注意只能是常量,为便于程序的阅读和修改,可以用#define
定义符号常量给出数组长度。
2.2 一维数组的访问
数组中包含按顺序存在的多个同类型元素,可以用数组名[下标]
的形式对数组中各个元素进行访问。下标(Subcripting)可以看成是数组元素的索引。
下标通常是整型的常量、变量、表达式,但需要注意的是:数组下标是从0开始编号的。因此,下标的有效取值范围为:0~数组长度-1。例如:
int score[5];
数组中包含了5个元素,它们的下标是0、1、2、3、4,分别对应score[0]
、score[1]
、score[2]
、score[3]
、score[4]
这5个元素,每个元素都是一个int
类型的变量。
/*函数功能:输出一维数组
函数参数:两个形式参数分别表示待输出的数组、实际输出的元素个数
函数返回值:无*/
void printarr(int a[}, int n)
{
int i;
printf("The elements are:\n");
for ( i=0;i<n;i++)
printf("%5d",a[i]);
printf("\n");
}
2.3 一维数组的赋值
同类数据集合的数组,其输入、输出和赋值操作只能对单个元素进行,我们往往借助于循环语句对数组元素逐个进行相同操作,即用循环实现对数组所有元素的遍历。
for ( i=0;i<num;i++)
{
scanf("%f",&score[i]); /*逐个输入学生的成绩*/
数组间赋值:
for ( i=0;i<5;i++)
a2[i]=a1[i];
2.4 一维数组的初始化
数组定义后,各个数组元素在内存中是连续存放的,但值是随机的。可以通过初始化数组给定元素初值。
初始化是指定义数组的同时直接对数组元素按从左到右的方式依次进行赋初值,例如:
int score[5]={198,95,67,83,761};
注意:
- 允许数组的局部初始化,未初始化的默认初始化为0。初值的个数可以 少于但不能多于数组允许的元素个数,且中间不能空。
- 初始化时允许省略数组的长度。
譬如:int score[]={98,95,67,83,76};
数组的大小不可变,而是由编译器根据所提供元素的数量来确定数组长度。本例中数组的长度为5
2.5 求解数组最大最小值
以最小值为例
/*函数功能:求一维数组中最小的元素*
函数参数:第1个参数对应待传递的数组,第2个整型参数表示数组的实际长度
函数返回值:数组元素中的最小值
*/
int minnum(int a[] , int n)
{
int i, min;
min=a[0] ; /*假定第1个元素是最小值*/
for ( i=1;i<n;i++) /*从第2个元素到第n个元素与当前的最小值比较*/
if (a[i]<min)
min=a[i]; /*当前元素值若小于min,则将值赋给min变量*/
return min;
2.6 一维数组的输出
/*函数功能:完成一维数组的输出
函数参数: 两个形式参数分别表示待输出的数组、实际输出的元素个数
函数返回值:无返回值
*/
void print(int a[],int n)
{
int i;
printf("The array is:\n");
for (i=0;i<n;i++)
printf("%5d",a[i]);
printf("\n");
}
三 二维数组
C语言不仅支持一维数组,也支持多维数组,最常见的是二维数组。二维数组具有两个下标值,可以表示数组在两个维度上的长度。
3.1 二维数组的定义
①二维数组的定义格式如下:
类型标识符 数组名[整型常量表达式1][整型常量表达式2];
int score[3][2];
②类型标识符表示该数组中的数组元素的类型,各个元素的数据类型都是一致的;
③整型常量表达式1
为行数,整型常量表达式2
为列数,这是与数学上的行列式相对应的。和一维数组一样,行、列下标值的范围分别是[0,行数-1]和[0,列数-1]。
3.2 二维数组的访问
二维数组中的每个元素是按行存储,只要利用两个循环控制变量分别表示行下标及列下标,即用嵌套循环就可以对数组进行访问,
for ( i=0;i<N;i++)
{
for( j=0;j<N;j++)
printf( "%5d",A[i]{j]);
printf("\n");
}
3.4 二维数组的初始化
C语言采用行优先的方式存储数组元素:即先存储第1行的元素,再依次存储第2、3⋯⋯行的元素。
- 全部元素均初始化
格式:逐行给出或者一次将全部值放在一对花括号
int grade[3] [4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}} ;//格式①
int grade[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};//格式②
-
对部分元素初始化
例如:int grade[3][4]={ 1,2,3,4,5,6 };
、int grade[3][4]={{1,2},{3,4},{5,6}};
后面没有初始化的元素默认值为0,仍然要求中间不能有空 -
省略行数
与一维数组类似,在初始化时可以省略二维数组的行数,但是不能省略列数,系统将自动根据数据个数与列数计算出行数,满足:(行数-1)*列数<数据个数≤行数*列数
。例如:
int grade[][4]={1,2,3,4,5,6,7,8,9,10,11,12};/*行数=12/4=3*/
int grade[][4]={1,2,3,4,5,6,7,8,9,10};/*行数=10/4+1=3*/
int grade[][4]={1,2,3,4,5,6,7,8};/*行数=8/4=2*/
四 数组的初步应用
4.1 二维数组的转置
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define ROW 3 /*原始矩阵的行数*/
#define COL 4 /*原始矩阵的列数*
int main()
{
int array_a[ROW][ COL]; /*用来存储原始矩阵*/
int array_b[ COL][ROW]; /*用来存储转置后的矩阵*/
int i,j;
srand(time(NULL)); /*使用系统时钟作为随机数种子*/
for(i=0;i< ROW;i++)
{
for(j=0;j< COL;j++)
array_a[i][j]=rand()%100+1; /*生成[1,100]的随机数*/
}
printf("Before transpose:\n");
for(i=0;i< ROW;i++) /*控制行*/
{
for(j=0;j< COL;j++) /*控制列*/
printf("%4d",array_a[i][j]);
printf("\n");
}
for(i=0;i< COL;i++) /*控制新矩阵的行*/
for(j=0;j< ROW;j++) /*控制新矩阵的列*/
array_b[i][j]=array_a[j][i]; /*注意array_a访问方法*/
printf("After transpose:\n");
for(i=0;i< COL;i++)
{
for(j=0;j< ROW;j++)
printf("%4d",array_b[i][j]);
printf("\n");
}
return 0;
}
①本例中首次用到了随机函数rand,使得二维数组中的原始数据除了初始化和用键盘输入之外,又有了一种新的方式——调用随机函数获得随机值赋值给数组元素。分案要产生[a,b]范围内的随机整数,则可以使用式子:rand()%(b-a+1)+a得到,本例中的a 为1,b为100.所以rand()%100+1产生的就是[1,100]范围内的整数。需要注意的是, rand 函数产生的是伪随机整数(每次运行得到的随机数都是确定的序列)。
强酮
②本例中配合函数rand还用到了另外两个函数:srand和time,为了产生数据的随机性更好,在第一次调用rand之前可以调用一次srand函数,其参数为time(NULL),即调用时间函数time来获取计算机系统的当前时钟。显然,每次运行程序系统时钟一定是不一样的,因此time(NULL)得到不同的值, srand 也就获得了不同的结果,从而接下来调用rand函数获得的随机数序列将会随着每次运行调用时间的变化而有所区分。
4.2 数组元素查找
最简单的方法就是从第一个元素开始依次与待查找的元素进行比较,如果相等就查找成功,输出元素及对应下标;如果与所有元素都比较结束仍没有相等的元素,则输出“元素不存在”的提示信息。
/*函数功能: 完成一维数组的查找算法
函数参数: 3个形式参数分别对应于待查找的数组、数组的有效元素个数以及
待查找的值
函数返回值:返回查询结果,若查询成功返回数组元素所在下标,不成功则返回数组长度值n*/
int find(int a[],int n,int x)
{
int i=0;
while(i<n) /*循环条件为:如果未找到且未搜索完元素*/
{
if (x==a[i]) /*如果查找成功,i的值正好是元素下标*/
break;
i++;
}
return i;
}
4.3 插入数组元素
插入算法的一般步骤:
①定位:即确定新元素的插入位置。
②移位:如果数组原来有n个元素,则共有n+1个可能的插入位置。
方法是:将下标为n-1的元素到下标为i的元素依次做赋值给后一个元素的操作。
③插入:插入新元素,即作一次赋值操作。
/*函数功能:完成一维数组的插入算法
函数参数: 3个形式参数分别对应于待插入的数组、现有元素个数、待插入元素
函数返回值:无返回值
*/
void insert(int a[],int n,int x)
{
int i,j;
for (i=0;i<n&&a[i]<x;i++);/*定位:查找待插入的位置i,循环停止时的i就是*/
for (j=n-1;j>=i;j--) /*移位:用递减循环移位,使i下标元素可被覆盖*/
a[j+1]=a[j];
a[i]=x; /*插入:数组的i下标元素值赋值为插入的x*/
}
程序实现时要注意,插入数据要有空余的空间,因此定义数组时其大于数组的初始有效元素个数。
4.4 删除数组元素
内存空间中的数据只能修改,不能“擦除”。所谓“删除”其实是通过将需要删除的元素“覆盖”完成的,也就是将待删除元素后面的元素依次赋值给前一个元素。
删除算法的一般步骤如下:
①定位:即确定待删除元素的下标。
②移位:如果待删除的元素下标为i,则通过一个递增型循环,从i下标开始一直到n-2下标依次将元素前移
③个数减1
int delArray(int a[],int n,int x)
{
int i,j;
int flag=1; /*是否找到待删元素的标志位,1找到,0未找到*/
for (i=0;i<n &&a[i]!=x;i++) ; /*查找x是否存在,此处循环体为空语句*/
if (i==n) /*循环停止时如果i==n,则说明元素不存在*/
flag=0;
else
{
for (j=i;j<n-1 ;j++)
a[j]=a[j+1]; /*前移覆盖i下标的元素*/
}
return flag;
}
本程序只能实现删除第1个值等于x的元素,如果存在多个与x值相同的元素,即找到第一个值为x的元素,完成覆盖后,应在该元素所在位置开始继续上述的查找、删除过程,直到所有元素全部遍历。
int delArray2(int a[],int n,int x)
{
int i,j;
int count=0; /*存储删除的个数*/
for (i=0;i<n-count;i++) /*每删除一个数x,数组有效长度会减少一个*/
{
if (a[i]==x)
{
count++;
for (j=i;j<n-count ;j++) /*找到删除的x,把后续数组元素向前覆盖一个*/
a[j]=a[j+1];
i--; /*迁移的数要重新判断*/
}
}
return count;
}
4.5 数组排序
排序可以用很多种方法实现,这里介绍其中的一种——冒泡排序。
其程序思想是:在排序过程中对元素进行两两比较,越小的元素会经由交换慢慢“浮”到数组的前面(低下标处),像气泡一样慢慢浮起,由此得名。
/*函数功能:完成一维数组的冒泡排序算法
函数参数: 两个参数分别是待排序数组及当前元素个数
函数返回值:无返回值
*/
void BubbleSort(int a[], int n)
{
int i, j, temp;
for (i = 0; i < n - 1; i++) /*共进行n-1趟排序*/
for (j = n - 1; j > i; j--) /*递减循环,从后往前比较*/
if (a[j] < a[j - 1]) /*两两比较,若后一个元素小则交换该组相邻元素*/
{
temp = a[j - 1];
a[j - 1] = a[j];
a[j] = temp;
}
}
改进
冒泡排序中,无论数列是否有序,都会一直比较直到最后两个元素。可以设置一个标志,如果这一趟发生了交换,将flag赋值为1,否则将flag赋值为0。因为如果有一趟没有发生交换,说明排序已经完成。
void BubbleSort(int a[], int n)
{
int j, k;
int flag=1;
int temp;
k = n;
while (flag)
{
flag =0;
for (j = 1; j < k; j++)
if (a[j - 1] > a[j])
{
temp=a[j-1];
a[j-1]=a[j];
a[j]=temp;
flag = 1;
}
k--;
}
}
程序还可以做进一步的优化。如果有100个数的数组,仅前面10个无序,后面90个都已排好序且都大于前面10个数字,那么在第1趟遍历后,最后发生交换的位置必定小于10,且这个位置之后的数据必定已经有序了,用flag变量记录下这位置,第2次只要从数组头部遍历到这个位置就可以了。
void BubbleSort(int a[], int n)
{
int j, k;
int flag,temp;
flag = n;
while (flag > 0)
{
k = flag;
flag = 0;
for (j = 1; j < k; j++)
if (a[j - 1] > a[j])
{
temp=a[j-1];
a[j-1]=a[j];
a[j]=temp;
flag = j;
}
}
}