集合类型
前面几章里,我们学习了四种基本数据类型,两种简单的自定义类型。这一章,我们学习一种自定义结构类型:集合类型。集合在数学中较常见,是若干个同类型数据的全部。在TURBO PASCAL中,集合的意义和数学中比较类似,但是为了编译的效率起见,TURBO PASCAL限制了集合的数量不能无限制的大,必须有一个范围。
一、集合的定义
在PASCAL中,我们把有限个同类数据的全体称为集合。为了体现它的全体性,我们用中括号把属于一个集合的数括起来。如:[1,2,3,4,9,-3,11],[‘A’,‘B’,‘C’,‘D’]。对于集合中的这些数据,规定必须是有序的。
集合定义的格式如下:
TYPE 集合类型名=SET OF 基本数据类型;
在这里,基本数据类型是指:字符型,整型中的子界型,布尔型,枚举型,子界型。整型和实型是不允许的,因为它们是无限集合。
例1:
TYPE
NUMBER=SET OF 1..100;
CH1=SET OF ‘A’..‘Z’;
CH2=SET OF CHAR;
COLOR=(RED,ORANGE,YELLOW,GREEN,INDIGO,BLUE,VIOLET);
CO1=SET OF COLOR;
以上定义了NUMBER,CH1,CH2,CO1四个集合。在定义CO1之前,先定义了枚举型COLOR。
VAR
A, B:NUMBER;
C:CH1;
D:CH2;
E,F:CO1;
这样,定义了6个集合变量A,B,C,D,E,F。下面可以对它们赋值:
A:=[34,17,22,8,66,22];
B:=[10,1,8,59];
C:=[‘H’,‘Q’,‘G’,‘U’,‘A’];
E:=[YELLOW,GREEN,VIOLET,BLUE]
F:=[]
集合变量的取值一定要和集合变量的类型一至。集合中的数据有以下特征:
1 集合中的数的个数是没有明确的限定,如有多个,则各个数据之间用逗号隔开。
2 集合中的数据允许重复出现,无论出现多少次,系统也只把它们当作一个数据看待。
3 集合中的数据是无序的,谁前谁后一个样。
4 集合中可以没有数据,我们称之为空集。
5 一个集合中的数据只能为同一种数据类型。
6 在集合中的数据如果是连续的有序型,则可以用子界型表示。如A:=[1..20]。
其实,在集合的定义部分,可以把集合类型定义和集合变量说明两步合并为一步进行:
VAR 集合变量名:SET OF 基本数据类型;
那么,前面的定义可以表示为:
VAR
A,B:SET OF 1..100;
C:SET OF ‘A’..‘Z’;
D:SET OF CHAR;
CO1:SET OF (RED,ORANGE,YELLOW,GREEN,INDIGO,BLUE,VIOLET);
二、集合类型的运算
2.1 赋值运算
赋值运算就是对已经定义好的集合变量进行赋值。如:
A:=[1,3,5,99];
B:=A;
C:=[];
其中,C是空集。
2.2 关系运算
在集合的关系运算里,有5种运算符:=,<>,>=,<=,IN。下面,我们来一一分析它们。
1) 相等判断(=,<>=)
“=”表示两集合相等,“<>”表求两集合不等。这个运算符是判断两同数据类型的集合是否相等。相等的依据是:两集合A,B中,A中的全部数据在B中都有,B中的全部数据在A中都有。这里没要求数据的先后顺序,也没要求两集合中数的个数必须相等。
例2 写出下面集合运算的结果。
[1,2,3,4]=[1,4,3,2,] TRUE
[3,6,9,4,7]<>[3,6,9,4] TRUE
[3,6,9,4,3]<>[6,9,4,3] FALSE
[]=[] TRUE
2) 包含运算(<=,>=)
在集合运算中,没有>,<,只有<=,>=。它们表示两个集合的包含关系。<=表示前者是后者的子集,>=表示后者是前者的子集。
例3 写出下列集合运算的结果。
[1,2,3,4]<=[1,2,3,4,5] TRUE
[1,2,3]<=[1,2,3] TRUE
[1,2,3]>=[1,2,3] TRUE
[1,2,3,4,5,6]>=[4,9] FALSE
[]<=[1] TRUE
我们说的子集关系是这样:如果A是B的子集,则A中所有数据在B中都能找到。空集是任何集合的子集。
3) IN 运算
上面的运算中进行运算的都是集合,而IN运算的意思是判断一个元素是否在一个集合内。作为一个元素,则在书写时不用加中括号了。
例4 写出下面运算的结果。
2 IN [1,2,3] TRUE
‘A’ IN [‘A’,‘C’,‘E’,‘G’] TRUE
6 IN [] FALSE
3 IN [4,6,9] FALSE
2.3 逻辑运算。
集合的逻辑运算和其它不一样:它运算出来不是布尔值,而是一个集合。集合的逻辑运算有三种:并(+),交(*),差(-)。
1 并
两集合A,B相并指的是有A,B两集合,将其中一个集合去掉它们的公共元素后再把二集合中的元素合并在一个集合里。并的操作用“+”表示。
例5
[1,2,3]+[2,3,4]=[1,2,3,4]
[1,5]+[2]=[1,2,5]
[1,4]+[1,4]=[1,4]
[5,6,7]+[]=[5,6,7]
2 交
两集合A,B相交指的是有A,B两集合,把它们的公共值取出来生成一个新的集合。交的操作用“*”表示。例6
[1,2,3]*[2,3,4]=[2,3]
[1,2,3]*[1,2,3]=[1,2,3]
[3,6,8]*[4,9]=[]
[1,2,3,4,5,6]*[3]=[3]
[RED,YELLOW,BIUE]*[YELLOE,VIOLET]=[YELLOW]
3 差
差的操作用“-”表示。两集合A,B的差指的是假设有A,B两集合,找出集合A,B中的公共元素,在集合A中减去公共元素后的结果便是A-B的差。
例7
[1,2,3,4]-[2,3,4]=[1]
[1,2,3,4]-[4,5,6,7]=[1,2,3]
[1,2,3,4]-[5,6,7,8]=[1,2,3,4]
[4,5,6]-[4,5,6]=[]
[4,7,9]-[]=[4,7,9]
当在一个表达式中,同时出现了+,-,*三种运算符时,其运算的先后顺序是先*,后+,-。+,-符号同级别,运算时依从左到右的顺序执行。
三、集合的应用
例1 集合运算.建立两个集合,然后对它们进行各种集合运算并输出运算结果。设该全集为[a,b,c,d,e,f],x=[a,b,c],y=[b,c,d],计算x+y,x*y,x-y,判断x是否包含y,d是否属于集合x-y。
算法分析:
1、 集合的建立:集合的建立可以通过赋值语句直接实现,也可以先初始化一个集合,然后利用集合的并运算逐一加入元素,形成所需集合。
2、 集合的运算:逻辑运算,关系运算。注意类型匹配。
3、 集合的输出:集合的输出必须通过测试输出,利用in 运算完成。
源程序如下:
Program jhys;
Type
letter=(a,b,c,d,e,f);
set1=set of letter;
Var
x,y,z1,z2,z3:set1;
c1,c2:Boolean;
i:letter;
Procedure setout(z:set1);
Var
ch:char;
k:letter;
Begin
write(‘[‘);
For k:=a to f do
If k in z then begin
Ch:=chr(ord(k)+ord(‘a’));
Write(ch:2);
End;
Writeln(‘]’);
End;
Begin
x:=[a,b,c];
y:=[];
for i:=b to d do y:=y+[i];
z1:=x+y;
z2:=x*y;
z3:=x-y;
c1:=x>=y;
c2:=d in z3;
Write(‘x=’);setout(x);
Write(‘y=’);setout(y);
Write(‘z1=’);setout(z1);
Write(‘z2=’);setout(z2);
Write(‘z3=’);setout(z3);
Writeln(‘x>=y is’,c1);
Writeln(‘d in x-y is’,c2);
Readln;
End.
例2 读入一串字符,以?结束。该字符串是数字和字母的组合。求字母和数字的个数。
分析:我们先设好两个集合,一个是‘0’-‘9’的数字集合,一个是‘A’到‘Z’的字母集合。每输入一个字符便判一次是属于哪个集合,然后得出结果。
PROGRAM e8(input,output);
VAR number:set of '0'..'9';
ch1:set of 'a'..'z';
ch2:set of 'A'..'Z';
n,c:integer;
ch:char;
BEGIN
ch1:=['a'..'z'];
ch2:=['A'..'Z'];
number:=['0'..'9'];
n:=0;
c:=0;
read(ch);
while ch<>'?' do
begin
if (ch in ch1) or (ch in ch2) then c:=c+1;
if ch in number then n:=n+1;
read(ch);
end;
writeln('number=',n,' ','char=',c);
END.
例3 将一16位的二进制数变成十进制数。
二进制变成十进制数的方法我们早已熟悉。我们编写一个函数专门求2的N次方,最后将它们相加。
PROGRAM e9(input,output);
FUNCTION CF(n:integer):longint;
var i:integer;
s:longint;
begin
s:=1;
if n=0 then cf:=1 else
begin
for i:=1 to n do
s:=s*2;
cf:=s;
end;
end;
VAR b:set of 0..15;
a,j:integer;
c:longint;
BEGIN
b:=[];
c:=0;
writeln('input a 16_bit number');
for j:=15 downto 0 do
begin
read(a);
if a=1 then b:=b+[j];
end;
for j:=0 to 15 do
if j in b then c:=c+cf(j);
writeln('c=',c);
END.
在这个程序中,在输入16位二进制数时,每输一个数要敲一个空格键。
例 4 将十进制整数k转换位二进制数,以集合类型变量buf表示一个16位二进制数,如18的二进制数位10010。则buf中记为[2,5]。即表示右起第2位,第5位是1,其余位为0。最后输出10010。
program ex4;
var
buf:set of 0..15;
i,k:integer;
begin
write(‘k=’);
readln(k);
buf:=[];
i:=0;
while (k<>0)and(k<>1) do begin
if odd(k) then buf:=buf+[i];
inc(i);
k:=k div 2;
end;
if k=1 then buf:=buf+[i];
k:=0;
for i:=15 downto 0 do
if i in buf then begin
k:=i;
break;
end;
for i:=k downto 0 do
if i in buf then write(‘1’)
else write(‘0’);
readln;
end.
例5 筛选法求素数。
算法分析:我们先看一个例子,通过此例子分析一下筛选法求素数的过程:
设n=20,即求2到20 之间的素数。
(1) 先把2到20 之间所有的数全部放入一个筛子中(即集合中);
(2) 在筛子中选择最小的元素i,把此数添加到集合p中p:=p+[i];
(3) 将素数i的所有倍数在集合中晒去。
for j:=1 to (n div i) do s:=s-[j*i];
(4) 如果筛子不空,则重复(2)、(3)步。我们将每一次的筛子集合s和素数集合p列出:
筛子集合s | 素数集合p |
|
[2..20] | [] |
|
[3,5,7,9.11,13,15,17,19] | [2] | i:=2 |
[5,7,11,13,17,19] | [2,3] | i:=3 |
[7,11,13,17,19] | [2,3,5] | i:=5 |
[11,13,17,19] | [2,3,5,7] | i:=7 |
[13,17,19] | [2,3,5,7,11] | i:=11 |
[17,19] | [2,3,5,7,11,13] | i:=13 |
[19] | [2,3,5,7,11,13,17] | i:=17 |
[] | [2,3,5,7,11,13,17,19] | i:=19 |
program primenumber;
const
N=200;
type
tgather=set of 2..N;
var
primes,sieve:tgather; {素数集合筛子}
total,i,j:integer; {计数器,素数,循环变量}
begin
sieve:=[2..N]; {将全部数放入筛子中}
primes:=[];
i:=2;
repeat {挑选筛中存在的最小数i(此数即为素数)}
while not (i in sieve) do inc(i);
{将素数i加入到素数集合primes}
primes:=primes+[i];
{在筛子sieve筛去素数i的倍数}
for j:=1 to (N div i) do sieve:=sieve-[j*i];
until sieve=[]; {直至筛子中为空}
{输出素数集合primes}
total:=0;
for i:=2 to N do
if i in primes then begin
inc(total);
write(i:4);
if total mod 10=0 then writeln;
end;
writeln(‘count=’,total); readln;
end.