编程珠玑 第一章 位图排序算法

位图排序是一种效率极高(复杂度可达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 }

测试数据产生文件:

View Code
 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 }
原文地址:https://www.cnblogs.com/bigrabbit/p/2657495.html