为什么要使用数组
例1 输入50个学生的某门课程的成绩,打印出低于平均分的同学号数与成绩。
【问题分析】
在解决这个问题时,虽然可以通过读入一个数就累加一个数的办法来求学生的总分,进而求出平均分。
但因为只有读入最后一个学生的分数以后才能求得平均分,且要打印出低于平均分的同学,故必须把50
个学生的成绩都保留下来, 然后逐个和平均分比较,把高于平均分的成绩打印出来。如果,用简单变量
a1,a2,…,a50 存放这些数据,可想而知程序要很长且繁。要想如数学中使用下标变量ai形式表示这50
个数,则可以引入下标变量a[i]。这样问题的程序可写为:
tot:=0; {tot表示总分}
for i:=1 to 50 do {循环读入每一个学生的成绩,并累加它到总分}
begin
read(a[i]);
tot:=tot+a[i];
end;
ave:=tot/50; {计算平均分}
for i:=1 to 50 do
if a[i]<ave then writeln(‘No.’,i,’ ‘,a[i]);{如果第i个同学成绩小于平均分,则输出}
而要在程序中使用下标变量,则必须先说明这些下标变量的整体―数组,即数组是若干个同名(如上
面的下标变量的名字都为a)下标变量的集合。
当数组中每个元素只带有一个下标时,我们称这样的数组为一维数组。
1、一维数组的定义
(1)类型定义
要使用数组类型等构造类型以及第5章学习的自定义类型(枚举类型与子界类型),应在说明部分进行
类型说明。 这样定义的数据类型适用整个程序。
类型定义一般格式为:
type
<标识符1>=<类型1>;
<标识符2>=<类型2>;
:
<标识符n>=<类型n>;
其中type是Pascal保留字,表示开始一个类型定义段。在其后可以定义若干个数据类型定义。<标识
符>是为定义的类型取的名字, 称它为类型标识符。
类型定义后,也就确定了该类型数据取值的范围,以及数据所能执行的运算。
(2)一维数组类型的定义
一维数组类型的一般格式:
array [下标1..下标2] of <基类型>;
说明:其中array 和of 是pascal 保留字。下标1 和下标2 是同一顺序类型,且下标2 的序号大于
下标1的序号。它给出了数组中每个元素(下标变量) 允许使用的下标类型,也决定了数组中元素的个数。
基类型是指数组元素的类型,它可以是任何类型,同一个数组中的元素具有相同类型。因此我们可以说,
数组是由固定数量的相同类型的元素组成的。
再次提请注意:类型和变量是两个不同概念,不能混淆。就数组而言,程序的执行部分使用的不是数
组类型(标识符)而是数组变量(标识符)。
一般在定义数组类型标识符后定义相应的数组变量,如:
type arraytype=array [1..8] of integer;
var a1,a2:arraytype;
其中arraytype为一个类型标识符,表示一个下标值可以是1到 8,数组元素类型为整型的一维数组;
而a1,a2则是这种类型的数组变量。
也可以将其合并起来:
var a1,a2:array [1..8] of integer;
当在说明部分定义了一个数组变量之后,pascal 编译程序为所定义的数组在内存空间开辟一串连续
的存储单元。
例如,设程序中有如下说明:
type rowtype=array [1..8] of integer;
coltype=array [’a’..’e’] of integer;
var a:rowtype;b:coltype;
2、一维数组的引用
当定义了一个数组,则数组中的各个元素就共用一个数组名( 即该数组变量名),它们之间是通过下
标不同以示区别的。 对数组的操作归根到底就是对数组元素的操作。一维数组元素的引用格式为:
数组名[下标表达式]
说明:①下标表达式值的类型, 必须与数组类型定义中下标类型完全一致,并且不允许超越所定义的
下标下界和上界。
②数组是一个整体,数组名是一个整体的标识,要对数组进行操作,必须对其元素操作。数组元素可
以象同类型的普通变量那样作用。如:a[3]:=34;是对数组a中第三个下标变量赋以34的值。read(a[4]);
是从键盘读入一个数到数组a第4个元素中去。
特殊地,如果两个数组类型一致,它们之间可以整个数组元素进行传送。如:
var a,b,c:array[1..100] of integer;
begin
c:=a;a:=b;b:=c;
end.
在上程序中,a,b,c三个数组类型完全一致, 它们之间可以实现整数组传送,例子中,先将a数组所
有元素的值依次传送给数组c,同样b 数组传给a,数组c 又传送给b,上程序段实际上实现了a,b 两个
数组所有元素的交换。
对于一维数组的输入与输出, 都只能对其中的元素逐个的输入与输出。在下面的应用示例中将详细介
绍。
3、一维数组元素的输入
不能整个数组输入,只能逐个元素赋值,A [ I ] := X ;
一般用FOR循环做。如:
FOR I := 1 TO 7 DO READ( A [ I ] );
一维数组元素的输出
不能整个数组一起输出,只能逐个元素输出,WRITE(A [ I ] );
一般用FOR循环做。如:
FOR I := 1 TO 7 DO WRITE( A [ I ] );
例2 输入50个数,要求程序按输入时的逆序把这50个数打印出来。也就是说,请你按输入相反顺序打印
这50个数。
【问题分析】我们可定义一个数组a用以存放输入的50个数, 然后将数组a内容逆序输出。
program ex5_1;
type arr=array[1..50]of integer; {说明一数组类型arr}
var a:arr;i:integer;
begin
writeln(’Enter 50 integer:’);
for i:=1 to 50 do read(a[i]); {从键盘上输入50个整数}
readln;
for i:=50 downto 1 do {逆序输出这50个数}
write(a[i]:10);
end.
例3、将a数组中第一个元素移到最后数组末尾,其余数据依次往前平移一个位置。
【问题分析】
为完成题目所要求的操作,其算法应该包括以下几个主要步骤:
①把第一个元素的值取出入在一个临时单元 temp中;
②通过 a[2]→a[1], a[3]→a[2], a[4]→a[3],……, a[n]→a[n-1],实现其余元素前移
③将 temp值送入a[n].
program p6-4;
const n=10;
var a:array[1..n] of integer;
i:integer; temp:integer;
begin
writeln(‘reak’,n,’datas’);
for i:=1 to n do read(a[i]);
temp:=a[1];
for i:=1 to n-1 do a[i]:=a[i+1];
a[n]:=temp;
writeln(‘Result:’);
for i:=1 to n do write(a[i]:3);
end.
运行结果 :
read 10 datas:
• 1 2 3 4 5 6 7 8 9 10
Result:
• 2 3 4 5 6 7 8 9 10 1
例4、输入十个正整数,把这十个数按由大到小的顺序排列。(选择排序 )
将数据按一定顺序排列称为排序,排序的算法有很多,其中选择排序是一种较简单的方法。
【问题分析】
要把十个数按从大到小顺序排列,则排完后,第一个数最大,第二个数次大,……。因此,我们第一
步可将第一个数与其后的各个数依次比较,若发现,比它大的,则与之交换,比较结束后,则第一个数已
是最大的数(最大的泡往上冒)。同理,第二步,将第二个数与其后各个数再依次比较,又可得出次大的
数。如此方法进行比较,最后一次,将第九个数与第十个数比较,以决定次大的数。于是十个数的顺序排
列结束。
如对5个进行排序,这个五个数分别为8 2 9 10 5。按选择排序方法,过程如下:
初始数据 :8 2 9 10 5
第一次排序:8 2 9 10 5
9 2 8 10 5
10 2 8 9 5
10 2 8 9 5
第二次排序:10 8 2 9 5
10 9 2 8 5
10 9 2 8 5
第三次排序:10 9 8 2 5
10 9 8 2 5
第四次排序:10 9 8 5 2
对于十个数,则排序要进行9次。源程序如下:
program ex5_2;
var a:array[1..10]of integer;
i,j,t:integer;
begin
writeln(’Input 10 integers:’);
for i:=1 to 10 do read(a[i]);{读入10个初始数据}
readln;
for i:=1 to 9 do {进行9次排序}
begin
for j:=i+1 to 10 do {将第i个数与其后所有数比较}
if a[i]<a[j] then {若有比a[i]大,则与之交换}
begin
t:=a[i];a[i]:=a[j];a[j]:=t;
end;
end;
for i:=1 to 10 do write(a[i]:5);
end.
例5、编程输入十个正整数,然后自动按从大到小的顺序输出。(冒泡排序)
【问题分析】
①用循环把十个数输入到A数组中;
②从A[1]到A[10],相邻的两个数两两相比较,即:
A[1]与A[2]比,A[2]与A[3]比,……A[9]与A[10]比。
只需知道两个数中的前面那元素的标号,就能进行与后一个序号元素(相邻数)比较,可写成通用形
式A[ i ]与A[ i +1]比较,那么,比较的次数又可用1~( n - i )循环进行控制 (即循环次数与两两相比
较时前面那个元素序号有关) ;
③在每次的比较中,若较大的数在后面,就把前后两个对换,把较大的数调到前面,否则不需调换位
置。
下面例举5个数来说明两两相比较和交换位置的具体情形:
5 6 4 3 7 5和6比较,交换位置,排成下行的顺序;
6 5 4 3 7 5和4比较,不交换,维持同样的顺序;
6 5 4 3 7 4和3比较,不交换,顺序不变
6 5 4 3 7 3和7比较,交换位置,排成下行的顺序;
6 5 4 7 3 经过(1~(5-1))次比较后,将3调到了末尾。
经过第一轮的1~ (N-1)次比较,就能把十个数中的最小数调到最末尾位置,第二轮比较1~ (N-2)次
进行同样处理,又把这一轮所比较的“最小数”调到所比较范围的“最末尾”位置;……;每进行一轮两
两比较后,其下一轮的比较范围就减少一个。最后一轮仅有一次比较。在比较过程中,每次都有一个“最
小数”往下“掉”,用这种方法排列顺序,常被称之为“冒泡法”排序。
Program Exam52;
const N=10;
Var a: array[1..N] of integer; {定义数组}
i,j: integer;
procedure Swap(Var x,y: integer); {交换两数位置的过程}
Var t:integer;
begin
t:=x; x:=y; y:=t
end;
Begin
for i:=1 to N do {输入十个数}
begin
Readln(a[ i ])
end;
for j:=1 to N-1 do {冒泡法排序}
for i:=1 to N-j do {两两相比较}
if a[ i ] < a[i+1] then swap(a[ i ], a[i+1]); {比较与交换}
for i:=1 to N do {输出排序后的十个数}
write(a[ i ]:6);
Readln
end.
例6、用筛法求出100以内的全部素数,并按每行五个数显示。
【问题分析】
⑴ 把2到100的自然数放入a[2]到a[100]中(所放入的数与下标号相同);
⑵ 在数组元素中,以下标为序,按顺序找到未曾找过的最小素数minp,和它的位置p(即下标号);
⑶ 从p+1开始,把凡是能被minp整除的各元素值从a数组中划去(筛掉),也就是给该元素值置 0;
⑷ 让p=p+1,重复执行第②、③步骤,直到minp>Trunc(sqrt(N)) 为止;
⑸ 打印输出a数组中留下来、未被筛掉的各元素值,并按每行五个数显示。
用筛法求素数的过程示意如下(图中用下划线作删去标志):
① 2 3 4 5 6 7 8 9 10 11 12 13 14 15…98 99 100 {置数}
② 2 3 4 5 6 7 8 9 10 11 12 13 14 15…98 99 100 {筛去被2整除的数 }
③ 2 3 4 5 6 7 8 9 10 11 12 13 14 15…98 99 100 {筛去被3整除的数 }
……
2 3 4 5 6 7 8 9 10 11 12 13 14 15…98 99 100 {筛去被整除的数 }
Program Exam53;
const N=100;
type xx=1 .. N; {自定义子界类型xx(类型名)}
Var a: array[xx] of boolean; i,j: integer;
Begin
Fillchar(a,sizeof(a),true);
a[1] := False;
for i:=2 to Trunc(sqrt(N)) do
if a[I] then
for j := 2 to N div I do
a[I*j]:= False;
t:=0;
for i:=2 to N do
if a[i] then
Begin
write(a[ i ]:5); inc(t);
if t mod 5=0 then writeln
end;
End.
例1 输入50个学生的某门课程的成绩,打印出低于平均分的同学号数与成绩。
【问题分析】
在解决这个问题时,虽然可以通过读入一个数就累加一个数的办法来求学生的总分,进而求出平均分。
但因为只有读入最后一个学生的分数以后才能求得平均分,且要打印出低于平均分的同学,故必须把50
个学生的成绩都保留下来, 然后逐个和平均分比较,把高于平均分的成绩打印出来。如果,用简单变量
a1,a2,…,a50 存放这些数据,可想而知程序要很长且繁。要想如数学中使用下标变量ai形式表示这50
个数,则可以引入下标变量a[i]。这样问题的程序可写为:
tot:=0; {tot表示总分}
for i:=1 to 50 do {循环读入每一个学生的成绩,并累加它到总分}
begin
read(a[i]);
tot:=tot+a[i];
end;
ave:=tot/50; {计算平均分}
for i:=1 to 50 do
if a[i]<ave then writeln(‘No.’,i,’ ‘,a[i]);{如果第i个同学成绩小于平均分,则输出}
而要在程序中使用下标变量,则必须先说明这些下标变量的整体―数组,即数组是若干个同名(如上
面的下标变量的名字都为a)下标变量的集合。
当数组中每个元素只带有一个下标时,我们称这样的数组为一维数组。
1、一维数组的定义
(1)类型定义
要使用数组类型等构造类型以及第5章学习的自定义类型(枚举类型与子界类型),应在说明部分进行
类型说明。 这样定义的数据类型适用整个程序。
类型定义一般格式为:
type
<标识符1>=<类型1>;
<标识符2>=<类型2>;
:
<标识符n>=<类型n>;
其中type是Pascal保留字,表示开始一个类型定义段。在其后可以定义若干个数据类型定义。<标识
符>是为定义的类型取的名字, 称它为类型标识符。
类型定义后,也就确定了该类型数据取值的范围,以及数据所能执行的运算。
(2)一维数组类型的定义
一维数组类型的一般格式:
array [下标1..下标2] of <基类型>;
说明:其中array 和of 是pascal 保留字。下标1 和下标2 是同一顺序类型,且下标2 的序号大于
下标1的序号。它给出了数组中每个元素(下标变量) 允许使用的下标类型,也决定了数组中元素的个数。
基类型是指数组元素的类型,它可以是任何类型,同一个数组中的元素具有相同类型。因此我们可以说,
数组是由固定数量的相同类型的元素组成的。
再次提请注意:类型和变量是两个不同概念,不能混淆。就数组而言,程序的执行部分使用的不是数
组类型(标识符)而是数组变量(标识符)。
一般在定义数组类型标识符后定义相应的数组变量,如:
type arraytype=array [1..8] of integer;
var a1,a2:arraytype;
其中arraytype为一个类型标识符,表示一个下标值可以是1到 8,数组元素类型为整型的一维数组;
而a1,a2则是这种类型的数组变量。
也可以将其合并起来:
var a1,a2:array [1..8] of integer;
当在说明部分定义了一个数组变量之后,pascal 编译程序为所定义的数组在内存空间开辟一串连续
的存储单元。
例如,设程序中有如下说明:
type rowtype=array [1..8] of integer;
coltype=array [’a’..’e’] of integer;
var a:rowtype;b:coltype;
2、一维数组的引用
当定义了一个数组,则数组中的各个元素就共用一个数组名( 即该数组变量名),它们之间是通过下
标不同以示区别的。 对数组的操作归根到底就是对数组元素的操作。一维数组元素的引用格式为:
数组名[下标表达式]
说明:①下标表达式值的类型, 必须与数组类型定义中下标类型完全一致,并且不允许超越所定义的
下标下界和上界。
②数组是一个整体,数组名是一个整体的标识,要对数组进行操作,必须对其元素操作。数组元素可
以象同类型的普通变量那样作用。如:a[3]:=34;是对数组a中第三个下标变量赋以34的值。read(a[4]);
是从键盘读入一个数到数组a第4个元素中去。
特殊地,如果两个数组类型一致,它们之间可以整个数组元素进行传送。如:
var a,b,c:array[1..100] of integer;
begin
c:=a;a:=b;b:=c;
end.
在上程序中,a,b,c三个数组类型完全一致, 它们之间可以实现整数组传送,例子中,先将a数组所
有元素的值依次传送给数组c,同样b 数组传给a,数组c 又传送给b,上程序段实际上实现了a,b 两个
数组所有元素的交换。
对于一维数组的输入与输出, 都只能对其中的元素逐个的输入与输出。在下面的应用示例中将详细介
绍。
3、一维数组元素的输入
不能整个数组输入,只能逐个元素赋值,A [ I ] := X ;
一般用FOR循环做。如:
FOR I := 1 TO 7 DO READ( A [ I ] );
一维数组元素的输出
不能整个数组一起输出,只能逐个元素输出,WRITE(A [ I ] );
一般用FOR循环做。如:
FOR I := 1 TO 7 DO WRITE( A [ I ] );
例2 输入50个数,要求程序按输入时的逆序把这50个数打印出来。也就是说,请你按输入相反顺序打印
这50个数。
【问题分析】我们可定义一个数组a用以存放输入的50个数, 然后将数组a内容逆序输出。
program ex5_1;
type arr=array[1..50]of integer; {说明一数组类型arr}
var a:arr;i:integer;
begin
writeln(’Enter 50 integer:’);
for i:=1 to 50 do read(a[i]); {从键盘上输入50个整数}
readln;
for i:=50 downto 1 do {逆序输出这50个数}
write(a[i]:10);
end.
例3、将a数组中第一个元素移到最后数组末尾,其余数据依次往前平移一个位置。
【问题分析】
为完成题目所要求的操作,其算法应该包括以下几个主要步骤:
①把第一个元素的值取出入在一个临时单元 temp中;
②通过 a[2]→a[1], a[3]→a[2], a[4]→a[3],……, a[n]→a[n-1],实现其余元素前移
③将 temp值送入a[n].
program p6-4;
const n=10;
var a:array[1..n] of integer;
i:integer; temp:integer;
begin
writeln(‘reak’,n,’datas’);
for i:=1 to n do read(a[i]);
temp:=a[1];
for i:=1 to n-1 do a[i]:=a[i+1];
a[n]:=temp;
writeln(‘Result:’);
for i:=1 to n do write(a[i]:3);
end.
运行结果 :
read 10 datas:
• 1 2 3 4 5 6 7 8 9 10
Result:
• 2 3 4 5 6 7 8 9 10 1
例4、输入十个正整数,把这十个数按由大到小的顺序排列。(选择排序 )
将数据按一定顺序排列称为排序,排序的算法有很多,其中选择排序是一种较简单的方法。
【问题分析】
要把十个数按从大到小顺序排列,则排完后,第一个数最大,第二个数次大,……。因此,我们第一
步可将第一个数与其后的各个数依次比较,若发现,比它大的,则与之交换,比较结束后,则第一个数已
是最大的数(最大的泡往上冒)。同理,第二步,将第二个数与其后各个数再依次比较,又可得出次大的
数。如此方法进行比较,最后一次,将第九个数与第十个数比较,以决定次大的数。于是十个数的顺序排
列结束。
如对5个进行排序,这个五个数分别为8 2 9 10 5。按选择排序方法,过程如下:
初始数据 :8 2 9 10 5
第一次排序:8 2 9 10 5
9 2 8 10 5
10 2 8 9 5
10 2 8 9 5
第二次排序:10 8 2 9 5
10 9 2 8 5
10 9 2 8 5
第三次排序:10 9 8 2 5
10 9 8 2 5
第四次排序:10 9 8 5 2
对于十个数,则排序要进行9次。源程序如下:
program ex5_2;
var a:array[1..10]of integer;
i,j,t:integer;
begin
writeln(’Input 10 integers:’);
for i:=1 to 10 do read(a[i]);{读入10个初始数据}
readln;
for i:=1 to 9 do {进行9次排序}
begin
for j:=i+1 to 10 do {将第i个数与其后所有数比较}
if a[i]<a[j] then {若有比a[i]大,则与之交换}
begin
t:=a[i];a[i]:=a[j];a[j]:=t;
end;
end;
for i:=1 to 10 do write(a[i]:5);
end.
例5、编程输入十个正整数,然后自动按从大到小的顺序输出。(冒泡排序)
【问题分析】
①用循环把十个数输入到A数组中;
②从A[1]到A[10],相邻的两个数两两相比较,即:
A[1]与A[2]比,A[2]与A[3]比,……A[9]与A[10]比。
只需知道两个数中的前面那元素的标号,就能进行与后一个序号元素(相邻数)比较,可写成通用形
式A[ i ]与A[ i +1]比较,那么,比较的次数又可用1~( n - i )循环进行控制 (即循环次数与两两相比
较时前面那个元素序号有关) ;
③在每次的比较中,若较大的数在后面,就把前后两个对换,把较大的数调到前面,否则不需调换位
置。
下面例举5个数来说明两两相比较和交换位置的具体情形:
5 6 4 3 7 5和6比较,交换位置,排成下行的顺序;
6 5 4 3 7 5和4比较,不交换,维持同样的顺序;
6 5 4 3 7 4和3比较,不交换,顺序不变
6 5 4 3 7 3和7比较,交换位置,排成下行的顺序;
6 5 4 7 3 经过(1~(5-1))次比较后,将3调到了末尾。
经过第一轮的1~ (N-1)次比较,就能把十个数中的最小数调到最末尾位置,第二轮比较1~ (N-2)次
进行同样处理,又把这一轮所比较的“最小数”调到所比较范围的“最末尾”位置;……;每进行一轮两
两比较后,其下一轮的比较范围就减少一个。最后一轮仅有一次比较。在比较过程中,每次都有一个“最
小数”往下“掉”,用这种方法排列顺序,常被称之为“冒泡法”排序。
Program Exam52;
const N=10;
Var a: array[1..N] of integer; {定义数组}
i,j: integer;
procedure Swap(Var x,y: integer); {交换两数位置的过程}
Var t:integer;
begin
t:=x; x:=y; y:=t
end;
Begin
for i:=1 to N do {输入十个数}
begin
Readln(a[ i ])
end;
for j:=1 to N-1 do {冒泡法排序}
for i:=1 to N-j do {两两相比较}
if a[ i ] < a[i+1] then swap(a[ i ], a[i+1]); {比较与交换}
for i:=1 to N do {输出排序后的十个数}
write(a[ i ]:6);
Readln
end.
例6、用筛法求出100以内的全部素数,并按每行五个数显示。
【问题分析】
⑴ 把2到100的自然数放入a[2]到a[100]中(所放入的数与下标号相同);
⑵ 在数组元素中,以下标为序,按顺序找到未曾找过的最小素数minp,和它的位置p(即下标号);
⑶ 从p+1开始,把凡是能被minp整除的各元素值从a数组中划去(筛掉),也就是给该元素值置 0;
⑷ 让p=p+1,重复执行第②、③步骤,直到minp>Trunc(sqrt(N)) 为止;
⑸ 打印输出a数组中留下来、未被筛掉的各元素值,并按每行五个数显示。
用筛法求素数的过程示意如下(图中用下划线作删去标志):
① 2 3 4 5 6 7 8 9 10 11 12 13 14 15…98 99 100 {置数}
② 2 3 4 5 6 7 8 9 10 11 12 13 14 15…98 99 100 {筛去被2整除的数 }
③ 2 3 4 5 6 7 8 9 10 11 12 13 14 15…98 99 100 {筛去被3整除的数 }
……
2 3 4 5 6 7 8 9 10 11 12 13 14 15…98 99 100 {筛去被整除的数 }
Program Exam53;
const N=100;
type xx=1 .. N; {自定义子界类型xx(类型名)}
Var a: array[xx] of boolean; i,j: integer;
Begin
Fillchar(a,sizeof(a),true);
a[1] := False;
for i:=2 to Trunc(sqrt(N)) do
if a[I] then
for j := 2 to N div I do
a[I*j]:= False;
t:=0;
for i:=2 to N do
if a[i] then
Begin
write(a[ i ]:5); inc(t);
if t mod 5=0 then writeln
end;
End.