我的C实践(3):用宏和位运算来实现整数集合

    整数集合用一个位向量来表示。这里是无符号整数的集合,它适合存放范围在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:对整数集合的测试。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值