整数集合用一个位向量来表示。这里是无符号整数的集合,它适合存放范围在0~N-1之内的小整数,N是位向量的位数(这里为32位的unsigned int)。每个整数用集合中的一个位来表示,第i位为1(i=0~N-1),当且仅当整数i属于集合。
1、头文件set.h:整数集合的接口。
解释:
(1)singleset(i):由于要返回只含有一个整数i的集合,因此要将位向量的第i位置1,其余位均为0,只要将1右移i位即可。
(2)first_set_of_n_elements(n):要生成集合{0,1,...,n-1},就要将第0,1,...,n-1位置1,其余位置0。可将1右移n位,这样第n位为1,位向量的值为2**(n-1),这里**为幂。减1后就变成2**(n-1)-1,其第0,1,...,n-1位为1,其余位为0。
2、函数定义文件set.c:整数集合的各个函数实现。
解释:
(1)cardinality(SET x)用于返回集合中元素的个数,采用的方法每去掉一个最小元素,就让计数器增1。这里-x会对x取反并加1,x & -x获得x的最右边的位1(其余位变成0),x^(x & -x)会将x最右边的位1置成0,其余位不变,因此就去掉了集合中的最小元素。
(2)printset(SET z)用于打印集合中所有元素。它用forallelements(e, z)来遍历集合的每个元素,然后打印它。
(3)print_k_of_n(k,n)用于打印集合{0,1,...,n-1}的所有大小为k的子集。它先设置打印参数,然后用first_set_of_n_elements(k)生成初始的大小为k的子集z={0,1,...,k-1}。打印各个子集时,从初始的子集z开始,不断地应用next_set_of_n_elements(z)生成大小为k的新子集,并用printset打印它。一旦产生的集合为空集或包含了元素n,显然这时就不再是{0,1,...,n-1}的子集了,结束循环。
(4)关键看next_set_of_n_elements(SET x)的实现。它必须保证生成大小为k但与z不相同的子集,同时要保证当生成空集或生成包含了元素n的集合时,{0,1,...,n-1}的所有的大小为k的子集(个数为n!/(k!(n-k)!)都已经产生。采用的算法在注释中已经描述得比较清楚了。看一个例子,对于{0,1,2,3,4}的大小为3的所有子集,从初始子集{0,1,2}开始,由于算法只对最小的连续相邻的数(构成一个位1组)进行移动,因此它能遍历到所有大小为3的子集。对组中最右边的2向左移一位,组中其余的位1移到最右端,得{0,1,3},不停地应用该算法,得{0,2,3},{1,2,3},{0,1,4},....,{2,3,4},再移动就得{0,1,5},生成含有元素5的集合,不再是子集了,因此循环就结束。
3、测试文件testset.c:对整数集合的测试。