编程珠玑第一章中的代码
使用位图法对七位正整数进行排序的算法。
#include <stdio.h> #include <stdlib.h> #include <time.h> #define BITSPERWORD 32 #define SHIFT 5 #define MASK 0x1F #define N 100 int a[1 + N/BITSPERWORD]; /* * i >> SHIFT :i / (1 << SHIFT) 即i / (1 << 5) = i / 32, 这样获取i在数组a中的下标值,如i=100,则100/32=3, * 所以100在数组a中的位置是3。 * 1 << (i & MASK) :确定应该在当前数组位置中的第几个二进制位设置为1。 * MASK的推导的过程: * 数字i在当前下标的数组内对应的二进制位是i - (i / 32) * 32,如i=100,就是100 - (100/32)*32 = 100 - 96 = 4 * 因为前面已经确定100在a[3]中,前面还有a[0],a[1],a[2]三个元素,这三个元素所占的二进制位数正好是96个,所以 * 100应该在a[3]中第4个二进制位。这是思路,下面说怎么确定MASK的值。 * 在计算机里加减乘除都是用补码进行的,正整数的补码等于整数本身,负整数的补码等于其反码加1,所以,以i=100为例 * i - (i / 32) * 32 = 100 - (100 / 32) * 32 = 100 - 96 = 100 + (-96) = 4,等价于 * i - ((i >> 5) << 5) = 100 - ((100 >> 5) << 5) * 为了方便书写,这里用单字节表示。 * = 0110 0100 - (0110 0100 & 1110 0000) * = 0110 0100 - (0110 0000) * = 100 + (-96) * -96的反码 = 0110 0000按位取反,即: 1001 1111 * 对应的补码= 反码加1,即: 1010 0000, 故, * = 0110 0100 + 1010 0000 = 1 0000 0100 * 结果的最高位多了一个1,这是溢出位,所以结果就是0000 0100 = 4 * 即 100 + (~(100 & 0xe0) + 1) = i + (~(i & 0xe0) + 1) == i & 0x1f * 所以MASK = 0x1F */ void set(int i) { a[i>>SHIFT] |= (1 << (i & MASK)); } void clr(int i) { a[i>>SHIFT] &= ~(1 << (i & MASK)); } int test(int i){ return a[i>>SHIFT] & (1 << (i & MASK)); } int main(void) { int i, count=100; srand((unsigned)time(NULL)); for (i = 0; i < N; i++) clr(i); while (count > 0) { i = rand() % 100; set(i); count--; } for (i = 0; i < N; i++) { printf("%d : ", i); if (test(i)) printf("%d", i); else printf("%d", 0); printf("\n"); } for(i = 0; i < (1 + N/BITSPERWORD); i++) { printf("%d : %d\n", i, a[i]); } return 0; }