数组是同类型数据的有序集合。数组由若干个元素组成,其中所有元素都于同一个基本数据类型,而且它们的先后次序是确定的。
8.1 一维数组
8.1.1 一维数组的定义
定义格式:
类型说明符 数组名[常量表达式]
数组的声明中包含了数组元素的数目和每个元素的类型。
例子:
int a[10];小标从0开始计数而不是从1开始计数
float b[5];
说明:
1)数组名命名规则遵从标识符命名规则并遵循见名知其意
2)数组名后为方括号[](取下标运算符),不能用圆括号。
3)定义格式中的“常量表达式”表示元素的个数,即数组长度。数组元素的小标(索引)始终从0开始,所以长度为n的数组元素的索引是从0到n-1。
4)数组定义格式中的“常量表达式”可以包含正整型常量和符号常量(仅限正整型)但不能包含变量
5)数组中的每个元素的数据类型相同,这些元素在内存里面是连续的
8.1.2 一维数组的初始化
1)定义数组时对所有元素赋初值。如int a[10]={0,1,2,3,4,5,6,7,8,9};其中花括号内的值依次赋给数组中各个元素
2)可以只给一部分元素赋初值。如int a[5]={1,3,5};只给前三个元素a[0]、a[1]、a[2]赋初值。后两个元素(a[3]、a[4])系统自动赋初值零
3)对全部数组元素赋初值时,可以不指定长度。如:int a[]={0,1,2,3,4};
相当于定义了一个含有5个元素的整型数组,其初值分别为0,1,2,3,4;当使用的空的方括号时,编译器会根据列表中的数值数目来确定数组的大小
4)指定初始化式的情况:数组中只有相对较少的元素需要进行显示的初始化,而其他元素可以默认赋值。
如:int a[10]={0,0,29,0,0,13,0,0,35,0};
int a[10]={[2]=29,[5]=13,[8]=35;}
下标运算符[]中的数字为指示符。指定初始化式中的指示符无顺序要求。
8.1.3 一维数组元素的引用
数组必须先定义后使用。只能逐个引用数组元素而不能一次引用整个数组,下表可以是整型常量或整型表达式。
例:数组元素的引用
#include "stdio.h"
main()
{
int i,a[10];
for(i=0;i<=9;i++)
{
a[i]=i;
}
for(i=9;i>=0;i--)
{
printf("%5d",a[i]);
}
}
例:一维数组的输入与输出
#include "stdio.h"
main()
{
int i;
int a[4];
printf("输入数组a(共4个整数):");
for(i=0;i<4;i++)
{
scanf("%d",&a[i]);
}
printf("\n输出数组a: ");
for(i=0;i<4;i++)
{
printf("a[%d]=%d ",i,a[i]);
}
}
例:引用数组输出Fibonacci数列的前20项
#include "stdio.h"
#define NUM 20
main()
{
int i;
int fib[NUM]={0,1};
for(i=2;i
例:求数组元素中的最大值与最小值
#include "stdio.h"
main()
{
int term[10];
int i,max,min;
printf("请输入10个整数:");
for(i=0;i<9;i++)
{
scanf("%d",&term[i]);
}
max=term[0];
min=term[0];
for(i=0;i<9;i++)
{
if(term[i]max)
{
max=term[i];
}
}
printf("最大数为%d",max);
printf("最小数为%d",min);
}
注意:
1)scanf和printf不能一次处理整个数组,只能逐个处理数组元素,当下标i取不同的值时,a[i]代表不同的数组元素,要用循环语句。
2)应用循环语句处理数组元素时,应正确控制下标变量的范围,如:
for(i=0;i<4;i++)或for(i=0;i<=3;i++)表示i从0变化到3
3)用输入语句获取数据给数组元素时,数组元素前应加上地址符号&。如:
scanf("%d",&a[i]);
注意:
数组可以作为函数的参数来传递。形参被说明为数组,对应的实参是数组的名字。C语言中,数组的名字不是一个变量,而是数组开头元素的地址(数组首地址),它是在编译时确定的一个地址常量。数组名x和x[0]是同一个地址值。
冒泡排序:
#include "stdio.h"
#define N 5
main()
{
int a[N],i,j,t;
printf("请输入%d个数字:",N);
for(i=0;ia[j+1])
{
t=a[j];
a[j]=a[j+1];
a[j+1]=t;
}
}
}
for(int i=0;i
基本思想:以由小到大排序为例,通过对相邻两个数进行比较并交换,使较大的数逐渐从队列的前面移到队列的后面。
算法分析:
1)首先在a[0]~a[n-1]的区间进行第一轮排序。首先比较a[0]和a[1],如果a[0]比a[1]大,则交换二者。然后比较a[1]和a[2];依次类推,直到比较a[n-2]和a[n-1]。第一轮排序结束后,最大的数就放在了a[n-1]的位置。
2)然后在a[0]~a[n-2]的区间进行第二轮排序;直到比较a[n-3]和a[n-2],第二大的数就放在了a[n-2]的位置。
3)最后一轮比较a[0]和a[1]。
内循环:依次比较相邻两个数
外循环:控制比较的轮数,循环轮数为N-1
线性查找:从数组的第一个元素开始,依次将要查找的数和数组中的元素比较,直到找到该数或找到整个数组为止。
8.2 二维数组和多维数组
8.2.1 二维数组和多维数组的概念及其定义
数组就是用于顺序存储同类型数据的数据结构。
如果有一个一维数组,它的每一个元素是类型相同的一位数组,数组的类型相同包括其大小相同并且各元素的类型相同,就可形成一个二维数组。
int age[3][5];
行下标、列下标。在内存中数组的元素是连续顺序存放的。
一维:先存下标为0的元素,再存下标为1的元素……
二维:先存放第一行,再存放第二行……
定义形式为:
类型标识符 数组名[常量表达式][常量表达式];
例:int i,a[3],b[3][4],c[2][5];
多维数组的定义int a[2][3][2];
假设有一个m×n的二维数组a,其中第i行第j列元素a[i][j]在数组中的位置计算公式:i×n+j+1。
8.2.2 二维数组的初始化
1)分别给二维数组赋初值;如:
int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
2)可以将所有数据写在一个花括号内,按数组排列的顺序对各元素赋初值
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
3)可以对部分元素赋初值,如
int a[3][4]={{1},{5},{9}};
作用是只对各行第1列的元素赋初值,其余元素值自动为0
例如:
int a[3][4]={{1},{0,6},{0,0,11}};
int a[3][4]={{1},{5,6}};
int a[3][4]={{1},{},{9}};
4)如果对全部元素都赋初值,则定义数组对第一维的长度可以不指定,但第二维的长度不能省略
int [3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
等价于:
int [][4]={1,2,3,4,5,6,7,8,9,10,11,12};
在定义时也可以只对部分元素赋初值而省略第一维的长度,但应分行赋初值
int a[][4]={{0,0,3},{},{0,10}};
5)指定初始化式对多维数组初始化
例子:
int a[3][4]={[1][2]=2,[2][3]=3,[0][2]=7,[1][0]=6};
按照指示符对指定的元素进行赋值,没有指定值得元素都默认是0
8.2.3 二维数组和多维数组的引用
数组必须先定义,后引用。
引用二维数组的形式:
数组名[下标][下标];
例如:a[0][2]表示引用二维数组a中第1行第3列元素
每个下标都应当分别用方括号括起来,下标是整型表达式。
例:向一个三维数组输入值并输出此数组全部元素
#include "stdio.h"
main()
{
int i,j,k,a[2][3][2];
for(i=0;i<2;i++)
{
for(j=0;j<3;j++)
{
for(k=0;k<2;k++)
{
scanf("%d",&a[i][j][k]);
}
}
}
for(i=0;i<2;i++)
{
for(j=0;j<3;j++)
{
for(k=0;k<2;k++)
{
printf("%3d",a[i][j][k]);
}
}
}
}
8.2.4 二维数组的经典实例
例子:求一个4*4矩阵元素的最小值
#include "stdio.h"
main()
{
static int a[4][4]={{12,76,4,1},{-19,28,55,-6},{2,10,13,-2},{3,-9,112,111}};
int i,j,row,colum,min; //row-行 colum-列
min=a[0][0];
for(i=0;i<4;i++)
{
for(j=0;j<4;j++)
{
if(a[i][j]
例子:矩阵转置
#include "stdio.h"
#define ROW 3
#define COL 4
main()
{
int a[ROW][COL],b[COL][ROW];
int i,j;
printf("请输入矩阵a的元素(%d*%d):\n",ROW,COL);
for(i=0;i|
例子:课程平均分、学生平均分
#include
#include
void cour_ave(int a,int b,float score[]) //课程平均分
{
int course,stud;
float total1,aver1;
for(course=0;course
8.3 字符数组
8.3.1 字符数组的定义
字符数组的定义格式为:
char 数组名[常量表达式]
字符数组的定义方法:
1)char c[5];
定义了一个一维数组,包含5个元素,分别是:c[0]…c[5]
2)char ch[5][4]
定义了一个二维数组,包含5×4元素
3)char c[5]={'C','h','i','n','a'};
#include "stdio.h"
main()
{
char str[][5]={{'*','*','*','*','*'},{' ','*','*','*',' '},{' ',' ','*',' ',' '},
{' ','*','*','*',' '},{'*','*','*','*','*'}};
int i,j;
for(i=0;i<5;i++)
{
for(j=0;j<5;j++)
{
printf("%c",str[i][j]);
}
printf("\n");
}
}
8.3.2 字符数组的输入输出
对于字符数组,可以将其每个元素当作单个字符型变量使用,也可以利用整数数组对一个字符串进行输入输出。
例子:单个字符的输入输出
#include
#include
main()
{
char str[81],*sptr;
int i;
for(i=0;i<80;i++)
{
str[i]=getchar();
if(str[i]=='\n')
{
break;
}
}
str[i]='\0';
sptr=str;
while(*sptr)
{
putchar(*sptr++);
}
}
例子:整个字符串的输入输出
#include "stdio.h"
main()
{
char ch[5];
int i;
printf("请输入字符串:");
scanf("%s",ch);
printf("%s",ch);
}
1)程序定义了一个字符数组,并赋予一个字符串
2)其中最后一个元素'\0',是字符串的结束标志,输入时,系统自动加入;输出时不显示
3)字符串要用双引号括起来
4)输入输出字符出要用%s格式符,输入输出项用字符数组名
例子:整个字符串的输入输出
#include "stdio.h"
main()
{
char ch[5];
int i;
for(i=0;i<5;i++)
{
scanf("%c",&ch[i]);
}
for(i=0;i<5;i++)
{
printf("%c",ch[i]);
}
}
8.3.3 常用字符串函数
1. 字符数组输出函数puts
将一个字符串输出到终端;以\0结束的字符序列。
2. 字符数组输入函数gets
从终端输入一个字符串到字符数组
3. 字符串连接函数strcat
函数调用格式:
strcar(字符数组1,字符数组2)
连接两个字符数组中的字符串,字符数组2连接到字符数组1后面,结果放在字符数组1中。
该函数包含在string.h中;字符数组1要足够大,容纳连接后的新字符串
4. 字符串拷贝函数strcpy
函数调用格式:
strcpy(字符数组1,字符串2)
将字符串2复制到字符数组1中
注意:
1)字符数组1的长度>字符串2
2)字符数组1必须写成数组名形式(如a1);字符串2可以是字符数组名,也可以是一个字符串常量。如strcpy(a1,"ltx")
3)复制字符串2时连同字符串后面的\0一起复制到字符数组1中
4)不能用赋值语句将一个字符串常量或字符数组直接赋给一个字符数组
5)可以用strncapy函数复制字符串2中前面若干个字符到字符数组1中
strncapy函数:
strncapy(a1,a2,4);
将a2前面4个字符复制到字符数组a1中,取代a1中前4个字符
5.字符串比较函数strcmp
两个字符出的比较,不能用关系运算符"==",而只能用strcmp函数
调用格式:
strcmp(字符串1,字符串2)
作用:比较字符串1和字符串2,返回值是:
1)如果字符串1=字符串2,则返回函数值为0;
2)字符串1>字符串2,则返回值为一整数;
3)字符串1<字符串2,则返回值为一负数;
比较规则:两个字符串自左向右比较,按ASCII值比较大小,直到出现不同字符或遇到\n为止。如果全部字符相同,认为两个字符串相等;若出现不相同的字符,则以第一个不相同的字符的比较结果为准。
6. 测串长函数strlen
函数调用格式:
strlen(字符数组)
作用:求字符串长度。函数返回字符串中原有字符的个数,不包括\0
7.字符串转小写函数strlwr
作用:将字符串转换成小写
8. 字符串转大写函数strupr
作用:将字符串转换成大写
8.3.4 字符数组的使用
例子:把字符串中所有的字符右移一个位置,串中的最后一个字符移到最前面
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
main()
{
char s[80],temp;
int i,len;
system("cls");
//clrscr();
printf("请输入字符:");
gets(s);
len=strlen(s);
temp=s[len-1];
for(i=len-1;i>0;i--)
{
s[i]=s[i-1];
}
s[0]=temp;
puts(s);
}
例子:利用二维字符数组对给定的字符串排序
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#define NUM 5
main()
{
char temp[10];
char nation[][10]={"Russia","France","Britain","America","China"}; //若干行 10列
int i,j,done;
system("cls");
for(i=0;i<5;i++)
{
printf("%s\t",nation[i]);
}
printf("\n");
for(i=0;i0) //比较;字1>字2为整数,否则是负数
{
strcpy(temp,nation[j]);
strcpy(nation[j],nation[j+1]);
strcpy(nation[j+1],temp);
done=1;
}
if(!done)
{
break;
}
}
}
for(i=0;i
清屏函数:clrscr();需要用头文件#include "conio.h"
或改写成:system("cls");需要头文件#include "stdlib.h"
8.3.5 常数数组
无论是一维数组、多维数组、字符数组,都可以通过在声明的开始部分加上关键字 const,而成为常量数组。
例子:
const char ch[]={'2','3','4','k'};
好处:
1)表明程序不可能改变数组元素的值,会给我们带来阅读理解的方便
2)助于编译器发现错误,const会告诉编译器,该数组元素的值不可能被修改
8.3.6 C99标准的变长数组
C99标准允许使用非常量表达式。
例子:
#include "stdio.h"
main()
{
int i,n;
scanf("%d",&n);
int a[n]; //数组长度定义为变量n
for(i=0;i
用变量来声明长度的数组称为变长数组,变长数组的长度是程序在执行时计算的。优点是程序在执行时可以精确地计算机出所需要元素地个数,避免数组可能过长,浪费内存、影响执行时间或过短地问题。
8.4 数组应用实例
8.4.1 排序
1. 直接插入排序法
按元素原来地顺序,先将下标为0地元素作为已排好的数据,然后从下标为1的元素开始,依次把后面的元素按大小插入到前面的元素中间,直到将全部元素插入完成。
#include "stdio.h"
#define N 5
main()
{
int i,j;
int a[N],t;
for(i=0;i=0&&t
2. Shell排序
冒泡排序法中每个相邻的元素都要进行比较,次数较多。
#include "stdio.h"
#define M 500
main()
{
int t,i,j,n,jump,count,times,a[M],alldone;
printf("请输入排序的总个数(n<=%d):\n",M);
scanf("%d",&n);
printf("输入%d个要在shell中排序的数字:\n",n);
for(i=0;i<=n-1;i++) //输入数字
{
scanf("%d",&a[i]);
}
count=times=0; //初始化比较的遍数和交换的次数
jump=n/2; //跳步间隔
while(jump>=1) //外层-控制跳步的大小
{
alldone=1;
while(alldone==1) //中间循环-跳步重复检查次数
{
alldone=0;
for(i=0;ia[j])
{
t=a[i];
a[i]=a[j];
a[j]=t;
alldone=1;
times++;
}
}
count++;
}
jump=jump/2;
}
for(i=0;i<=n-1;i++) //输出排序后的结果
{
printf("%3d",a[i]);
if((i+1)%10==0) //每10个为一行输出
{
printf("\n");
}
}
printf("\n比较遍数:%d\n交换次数:%d\n",count,times);
}
基本思想:允许第一次跳过较大的间隔去和后面的元素进行比较,当接近目的地,再跳过较小的和后面的元素进行比较,直到间隔为1才进行相邻元素的比较。开始时跳过的间隔是数组长度的1/2,以后再取上次的1/2。
例子:10个元素的数组,跳步共3种:10/2=5;5/2=2;2/2=1。即jump可以取值5、2、1。
Shell排序三重循环:
外层:控制跳步的大小;
内层:执行扫描、比较、交换;
中间:控制当前跳步下重复检查的次数,可设一个开关变量alldone来控制,其值为1表示进行内循环,其值为0表示不再进行内循环。n为数组长度,a为数组名。
8.4.2 二分查找
又称为对半查找。
#include "stdio.h"
#define N 10
main()
{
int k,i;
int table[N]={0,2,4,6,8,10,12,14,16,18};
int find=0; //标记,0是没找到 1是找到
int mid,left,right;
printf("%d,%d,%d",mid,left,right);
printf("请输入要查找的数:");
scanf("%d",&k);
while(!find&&left
假定数据是按升序排列,对给定值k,从序列的中间位置开始比较,如果当前位置值等于k,则查找成功;否则若k小于当前位置值,则在序列的前半段数据中继续查找;若k大于当前位置值,则在序列的后半段数据中继续查找,直到找到给定值k,则查找成功;或表示序列查找范围的上、下界数值颠倒。
例子:0 1 2 3 4 5 6 7 8 9
1)用left、right作为查找范围的上下界,用mid表示每次比较的数据对象的位置,mid是由left、right标记的查找范围的中间
mid=(left+right)/2
2)最初left=0,right=7,则mid=3,[]表示范围
[0 1 2 3 4 5 6 7 8 9]