题目描述:
位图法就是bitmap的缩写,是用每一个bit位来存放某种状态,适用于大规模数据的查找和排序。位图算法称的上是最简单的算法,只需要一个for循环就能够搞定排序和查找,但是同时它也是对待操作的数据要求最苛刻的,首先这些数据必须是正整数,且数据不能重复,其次要大致知道这些数据的范围,且有一定的聚集性。
数据结构为:unsigned int bitmap[N] ,其可以表示的数据范围是0~N*32-1
常用的操作:
置位:bitmap[n / 32] |= (1UL << (n & 31));//表示第n位置1
复位:bitmap[n / 32] &= ~(1UL << (n & 31));//表示第n位置0
获取第i位:puiBitmap[i / 32] & (1UL << (i & 31))
本题实现的功能为搜索指定区间内第uiNum个被占的位(即该位为1),本文采用两种方法来实现,一种是采用顺序查找的方法;另一种是在顺序查找的方法进行改进,先大范围查找,再小范围顺序查找。并对这两种方法的执行效率进行对比。
代码如下:
/* 搜索区间内第uiNum个被占的位(位为1) */ #include "stdafx.h" #include <stdint.h> #include <stdlib.h> #include <string.h> #include <ctime> #include <sys/timeb.h> #define MAX_VALUE 6000000 typedef unsigned int UINT32; /* 方法1:采用顺序查找的方法 */ UINT32 BitmapSearchNextBit1(UINT32 *puiBitmap, UINT32 uiStart, UINT32 uiEnd, UINT32 uiNum) { UINT32 i = 0; UINT32 num = 0; UINT32 max_value = 0; if (!puiBitmap || uiEnd < uiStart) return (UINT32)-1; max_value = (sizeof(puiBitmap) / sizeof(puiBitmap[0])) * 32 - 1; if (uiStart > max_value) return (UINT32)-1; for (i = uiStart; i <= uiEnd; i++) { if (puiBitmap[i / 32] & (1UL << (i & 31)))//判断某一位是否为1 num++; if (num == uiNum) return i; } return (UINT32)-1; } /* 方法2:下面对顺序查找的方法进行改进,在一个整数长度内(32位)内采用顺序查找,即小范围内采用顺序查找; 具体思路就是先以整形长度为单位,统计每个整形长度内1的个数,进而缩小查找范围,再采用以位为单位的顺序查找。 */ /* 获取一个整数的二进制表示中1的个数 */ static UINT32 get_one_count(UINT32 ui_num) { UINT32 i = 0; UINT32 sum = 0; while (i < 32) { sum += ((ui_num >> i) & 1UL); i++; } return sum; } /* 以位为单位进行小范围查找,获取指定范围内1的个数 */ static UINT32 BitmapSearchBitCount(UINT32 *puiBitmap, UINT32 uiStart, UINT32 uiEnd, UINT32 uiNum, UINT32 *count) { UINT32 i = 0; if (!puiBitmap || uiEnd < uiStart) return (UINT32)-1; for (i = uiStart; i <= uiEnd; i++) { if (puiBitmap[i / 32] & (1UL << (i & 31)))// 判断某一位是否为1 { (*count)++; } if (*count == uiNum) // 找到了,返回位索引 return i; } return (UINT32)-1; } UINT32 BitmapSearchNextBit2(UINT32 *puiBitmap, UINT32 uiStart, UINT32 uiEnd, UINT32 uiNum) { UINT32 count1 = 0; UINT32 count2 = 0; UINT32 count3 = 0; UINT32 temp_count = 0; UINT32 ret = 0; UINT32 index = 0; ret = BitmapSearchBitCount(puiBitmap, uiStart, (uiStart / 32 + 1) * 32 - 1, uiNum, &count1); if (ret != -1) return ret; for (index = uiStart / 32 + 1; index <= uiEnd / 32 - 1; index++) { temp_count = get_one_count(puiBitmap[index]); count2 += temp_count; if (count1 + count2 >= uiNum) { count2 -= temp_count; temp_count = 0; ret = BitmapSearchBitCount(puiBitmap, index * 32, (index + 1) * 32 - 1, uiNum - count1 - count2, &temp_count); if (ret != -1) return ret; } } temp_count = 0; ret = BitmapSearchBitCount(puiBitmap, uiEnd / 32 * 32, uiEnd, uiNum - count1 - count2, &temp_count); return ret; } int _tmain(int argc, _TCHAR* argv[]) { struct timeb startTime, endTime; UINT32 ret = 0;UINT32 puiBitmap[(MAX_VALUE + 31) / 32]; memset(puiBitmap,0,sizeof(puiBitmap)); printf("%d\n", puiBitmap[2]); //置位 puiBitmap[40 / 32] |= (1UL << (40 & 31)); puiBitmap[150 / 32] |= (1UL << (150 & 31)); puiBitmap[300 / 32] |= (1UL << (300 & 31)); puiBitmap[607 / 32] |= (1UL << (607 & 31)); puiBitmap[600000 / 32] |= (1UL << (6000000 & 31)); //复位 //puiBitmap[40 / 32] &= ~(1UL << (40 & 31)); ftime(&startTime); int i = 0; while (i < 10) { //ret = BitmapSearchNextBit1(puiBitmap, 20, 600, 1); printf("%d\n", ret); //ret = BitmapSearchNextBit1(puiBitmap, 0, 280, 2); printf("%d\n", ret); //ret = BitmapSearchNextBit1(puiBitmap, 20, 400, 3); printf("%d\n", ret); //ret = BitmapSearchNextBit1(puiBitmap, 20, 607, 4); //printf("%d\n", ret);ret = BitmapSearchNextBit1(puiBitmap, 20, 6000000, 5); //printf("%d\n", ret);i++; } ftime(&endTime);printf("cost time1=%d\n", (endTime.millitm - startTime.millitm) + (endTime.time - startTime.time) * 1000); printf("//\n"); ftime(&startTime); i = 0; while (i < 10) { //ret = BitmapSearchNextBit2(puiBitmap, 20, 600, 1); printf("%d\n", ret); //ret = BitmapSearchNextBit2(puiBitmap, 0, 280, 2); printf("%d\n", ret); //ret = BitmapSearchNextBit2(puiBitmap, 20, 400, 3); printf("%d\n", ret); //ret = BitmapSearchNextBit2(puiBitmap, 20, 607, 4); printf("%d\n", ret); ret = BitmapSearchNextBit2(puiBitmap, 20, 6000000, 5); //printf("%d\n", ret);i++;} ftime(&endTime); printf("cost time2=%d\n", (endTime.millitm - startTime.millitm) + (endTime.time - startTime.time) * 1000); system("pause"); return 0; }
运行结果如下图:
可见经过改进后算法在查找速度上有所提升。方法1程序简单,方法2实现复杂,在小范围内查找时两种方法没差别,在大范围内查找才会有所区别。