编程珠玑 第一章 位图排序算法
位图排序是一种效率极高(复杂度可达O(n))并且很节省空间的一种排序方法,但是这种排序方法对输入的数据是有比较严格的要求(数据不能重复,大致知道数据的范围)。位图排序即利用位图或者位向量来表示集合。举个例子,假如有一个集合{3,5,7,8,2,1},我们可以用一个8位的二进制向量set[1-8]来表示该集合,如果数据存在,则将set相对应的二进制位置1,否则置0.根据给出的集合得到的set为{1,1,1,0,1,0,1,1},然后再根据set集合的值输出对应的下标即可得到集合{3,5,7,8,2,1}的排序结果。这个就是位图排序的原理。
一.位图排序的应用:
1.给40亿个不重复的unsigned int的整数,没有排过序,然后再给一个数,如果快速判断这个数是否在那40亿个数当中。
因为unsigned int数据的最大范围在在40亿左右,40*10^8/1024*1024*8=476,因此只需申请512M的内存空间,每个bit位表示一个unsigned int。读入40亿个数,并设置相应的bit位为1.然后读取要查询的数,查看该bit是否为1,是1则存在,否则不存在。
2.给40亿个unsigned int的整数,如何判断这40亿个数中哪些数重复?
同理,可以申请512M的内存空间,然后读取40亿个整数,并且将相应的bit位置1。如果是第一次读取某个数据,则在将该bit位置1之前,此bit位必定是0;如果是第二次读取该数据,则可根据相应的bit位是否为1判断该数据是否重复。
二.位图排序的实现
由于在C语言中没有bit这种数据类型,因此必须通过位操作来实现。
假如有若干个不重复的正整数,范围在[1-100]之间,因此可以申请一个int数组,int数组大小为100/32+1。
假如有数据32,则应该将逻辑下标为32的二进制位置1,这个逻辑位置在A[1]的最低位(第0位)。
因此要进行置1位操作,必须先确定逻辑位置:字节位置(数组下标)和位位置。
字节位置=数据/32;(采用位运算即右移5位)
位位置=数据%32;(采用位运算即跟0X1F进行与操作)。
其他操作如清0和判断两个操作类似。
引自:http://www.cnblogs.com/dolphin0520/archive/2011/10/19/2217369.html
位图排序头文件:
1 #define true 1 2 #define false 0 3 typedef int bool; 4 void set_bit(unsigned int *bitmap,unsigned int k); 5 void clear_bit(unsigned int *bitmap,unsigned int k); 6 bool test_bit(unsigned int *bitmap,unsigned int k); 7 bool init_bitmap(unsigned int **bitmap,unsigned int n);
位图排序实现文件:
1 //bitmap_sort.c 文件 2 //实现位图排序功能 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <memory.h> 6 #include "bitmap_sort.h" 7 #define BITSPERWORD 32 8 #define SHIFT 5 9 #define MASK 0x1F 10 11 12 // 数字k所对应位图的位置置为1 13 void set_bit(unsigned int *bitmap,unsigned int k) 14 { 15 unsigned int byte_position; 16 unsigned short bit_position; 17 byte_position = k >> 5//byte_position表示k在位图中的字节 18 bit_position = k & 0x1F;//bit_position表示k所在字节的位 19 /*printf("byte = %d bit = %d\n", byte_position, bit_position);*/ 20 bitmap[byte_position] = bitmap[byte_position] | (1 << bit_position); 21 } 22 23 //数字k所对应位图的位置清零 24 void clear_bit(unsigned int *bitmap,unsigned int k) 25 { 26 unsigned int byte_position; 27 unsigned short bit_position; 28 byte_position = k >> 5; 29 bit_position = k & 0x1F; 30 /*printf("byte = %d bit = %d\n", byte_position, bit_position);*/ 31 bitmap[byte_position] = bitmap[byte_position] & ~(1 << bit_position); 32 } 33 34 //测试数字k所对应位图的位置为1或为0 35 bool test_bit(unsigned int *bitmap,unsigned int k) 36 { 37 unsigned int byte_position; 38 unsigned short bit_position; 39 byte_position = k >> 5; 40 bit_position = k & 0x1F; 41 /*printf("byte = %d bit = %d\n", byte_position, bit_position);*/ 42 return bitmap[byte_position] & (1 << bit_position); 43 } 44 45 //初始化位图 46 bool init_bitmap(unsigned int **bitmap, unsigned int n) 47 { 48 unsigned int size = 1 + n/BITSPERWORD, i; 49 printf("n = %u size = %u\n", n, size); 50 *bitmap = (unsigned int *)malloc(size*sizeof(unsigned int)); 51 if(*bitmap == NULL) 52 { 53 printf("Failed to malloc\n"); 54 return false; 55 } 56 //数据初始化 57 /*for(i = 1 ; i <=n; i ++) 58 clear_bit(*bitmap, i); 59 */ 60 //新开辟的空间均初始化为零 61 memset(*bitmap, 0, size*sizeof(unsigned int)); 62 return true; 63 }
位排序测试文件:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include "bitmap_sort.h" 4 //从文件proc读入数据,添加到位图中,文件的中格式是以空格隔开的,例如10 20 2。 5 //proc 读取文件指针 6 //n 位图大小 7 //bitmap 位图集合 8 bool in_process(FILE *proc, unsigned int n, unsigned int *bitmap) 9 { 10 unsigned int k; 11 printf("in process\n"); 12 while(!feof(proc)) 13 { 14 if(!fscanf(proc, "%u ", &k))//读取整数 15 { 16 printf("when input data from file, some error happened!\n"); 17 return false; 18 } 19 else if( k > n) //整数是否在范围内 20 { 21 printf("data upper overflow!\n"); 22 return false; 23 } 24 else 25 { 26 printf("data = %u\n", k); 27 set_bit(bitmap,k); 28 } 29 } 30 return true; 31 32 } 33 //从位图读取数据,写入到proc文件中 34 //proc 写入文件指针 35 //n 位图大小 36 //bitmap 位图集合 37 38 bool out_process(FILE *proc, unsigned int n, unsigned int *bitmap) 39 { 40 unsigned int i, k; 41 printf("out process\n"); 42 for(i = 1; i <= n; i ++) 43 { 44 if(!test_bit(bitmap, i)) 45 continue; 46 else if(feof(proc))//检测文件结束 47 { 48 printf("when output data into flie, some errors happened!\n"); 49 return false; 50 } 51 else 52 { 53 printf("data = %u\n", i); 54 fprintf(proc, "%u ", i); 55 } 56 57 } 58 return true; 59 } 60 61 //函数调用需要三个参数: 位图大小 输入文件 输出文件 62 int main(int argc, char *argv[]) 63 { 64 unsigned int *bitmap; 65 FILE *in, *out; 66 if(argc != 4) 67 { 68 printf("Usage: %s n infile outfile\n", argv[0]); 69 return -1; 70 } 71 72 73 unsigned int size = atoi(argv[1]); 74 75 if((in = fopen(argv[2], "r")) == NULL) 76 { 77 printf("open infile failed\n"); 78 fclose(in); 79 return -1; 80 } 81 if((out = fopen(argv[3], "w")) == NULL) 82 { 83 printf("open outfile failed\n"); 84 fclose(out); 85 return -1; 86 } 87 88 89 if(!init_bitmap(&bitmap, size)) 90 { 91 printf("init_bitmap failed\n"); 92 return -1; 93 } 94 95 if(!in_process(in, size, bitmap)) 96 { 97 printf("in_process failed\n"); 98 fclose(in); 99 free(bitmap); 100 return -1; 101 } 102 103 if(!out_process(out, size, bitmap)) 104 { 105 printf("in_process failed\n"); 106 fclose(in); 107 fclose(out); 108 free(bitmap); 109 return -1; 110 } 111 112 fclose(in); 113 fclose(out); 114 free(bitmap); 115 return 0; 116 117 }
测试数据产生文件:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include <assert.h> 4 //产生[lower upper]区间的随机整数 5 int randint(int lower, int upper) 6 { 7 assert(lower <= upper); 8 return rand()%(upper - lower + 1) + lower; 9 } 10 11 //交换set[lower - 1] 与 set [upper] 12 void swap(unsigned int *set, unsigned int x, int y ) 13 { 14 unsigned int element; 15 element = set[x - 1]; 16 set[x - 1 ] = set[y - 1]; 17 set[y - 1] = element; 18 } 19 20 //函数调用需要三个参数: 数据的最大值 数据数目 输出文件 21 int main(int argc, char *argv[]) 22 { 23 24 unsigned int *set; 25 FILE *file; 26 int i; 27 if(argc != 4) 28 { 29 printf("Usage: %s max size infile outfile\n", argv[0]); 30 return -1; 31 } 32 33 unsigned int max = atoi(argv[1]); 34 unsigned int size = atoi(argv[2]); 35 36 assert(size < max); 37 38 if((file = fopen(argv[3], "w")) == NULL) 39 { 40 printf("open infile failed\n"); 41 fclose(file); 42 return -1; 43 } 44 45 set = (unsigned int *)malloc(max * sizeof(unsigned int)); 46 47 if(set == NULL) 48 { 49 printf("Failed to malloc set\n"); 50 fclose(file); 51 return -1; 52 } 53 54 for(i = 1; i <= max; i ++) 55 set[i - 1] = i; 56 57 //产生size个[1, max]内不重复的整数,源自编程珠玑第一章习题4 58 for(i = 1; i <= size; i ++) 59 { 60 swap(set, i, randint(i, max)); 61 62 if(feof(file))//检测文件结束 63 { 64 printf("when output data into flie, some errors happened!\n"); 65 return 1; 66 } 67 else 68 { 69 printf("data[%u] = %u\n", i, set[i - 1]); 70 fprintf(file, "%u ", set[i - 1]); 71 } 72 73 } 74 75 fclose(file); 76 free(set); 77 return 0; 78 79 }