一、给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中
申请512M的内存
一个bit位代表一个unsigned int值
读入40亿个数,设置相应的bit位
读入要查询的数,查看相应bit位是否为1,为1表示存在,为0表示不存在
二、使用位图法判断整形数组是否存在重复
判断集合中存在重复是常见编程任务之一,当集合中数据量比较大时我们通常希望少进行几次扫描,这时双重循环法就不可取了。位图法比较适合于这种情况,它的做法是按照集合中最大元素max创建一个长度为max+1的新数组,然后再次扫描原数组,遇到几就给新数组的第几位置上1,如遇到 5就给新数组的第六个元素置1,这样下次再遇到5想置位时发现新数组的第六个元素已经是1了,这说明这次的数据肯定和以前的数据存在着重复。这种给新数组初始化时置零其后置一的做法类似于位图的处理方法故称位图法。它的运算次数最坏的情况为2N。如果已知数组的最大值即能事先给新数组定长的话效率还能提高一倍。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
bool hasDuplicatedItem(int *a, int len)
{
int length, max, i;
length = len;
max = a[0];
for(i = 1; i < length; i++){
if(a[i] > max)
max = a[i];
}
int *arr;
arr = (int*)malloc(sizeof(int) * (max + 1));
for(i = 0; i < length; i++){
if(arr[a[i]])
return true;
else
arr[a[i]] = 1;
}
return false;
}
int main()
{
int length;
int test[] = {0,1,2,3,45,12,13};
length = (sizeof(test) / sizeof(test[0]));
if(hasDuplicatedItem(test, length))
printf("hasDuplicatedItem!\n");
else
printf("hasNoDuplicatedItem!\n");
return 0;
}
三、使用位图法进行整形数组排序
首先遍历数组,得到数组的最大最小值,然后根据这个最大最小值来缩小bitmap的范围。这里需要注意对于int的负数,都要转化为unsigned int来处理,而且取位的时候,数字要减去最小值。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
void bitmapSort(int *a, int len)
{
int length, max, min, i, index;
length = len;
min = max = a[0];
//找出数组最大值
for(i = 1; i < length; i++){
if(a[i] > max){
max = a[i];
}
if(min > a[i]) {
min = a[i];
}
}
//得到位图数组
int *arr;
arr = (int*)malloc(sizeof(int) * (max - min + 1));
for(i = 0; i < length; i++){
index = a[i] - min;
arr[index]++;
}
//重整a中的元素
int arr_length;
arr_length = max - min + 1;
index = 0;
for(i = 0; i < arr_length; i++){
while(arr[i] > 0){
a[index] = i + min;
index++;
arr[i]--;
}
}
}
void print(int *a, int n)
{
int i;
for(i = 0; i < n; i++) {
printf("%d ", a[i]);
}
printf("\n");
}
int main()
{
int length;
int test[] = {50,1,26,3,45,12,13};
length = sizeof(test) / sizeof(test[0]);
print(test, length);
bitmapSort(test, length);
print(test, length);
return 0;
}
四、位图法存数据
输入:一个最多包含n个正整数的文件,每个数都小于n,其中n=10,000,000 输入文件中没有重复的整数,没有其他数据与该整数相关联。
输出: 按升序排列这些数。
约束:有 1MB多(不超过2MB) 的内存空间可用,有充足的硬盘空间。
#include<stdio.h>
#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 10000000
int a[1 + N/BITSPERWORD];
/* a[i>>SHIFT]是第i位应该在第几个int上 */
/* (1<<(i & MASK))是第i位在该int上的第几个bit */
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()
{
int i;
for(i = 0; i < N; i++)
clr(i);
while(scanf("%d", &i) != EOF)
set(i);
for(i = 0; i < N; i++)
if(test(i))
printf("%d\n", i);
return 0;
}
事实上,我们是用每一个 元素表示一个32位的二进制字符串,这样这个元素可以保留相邻32个号码是否存在的信息,数组范围就下降到10000000/32了.例如对于号码 89256,由于89256 mod 32=2789…8,这样我们应该置a[2789]中32位字符串的第8位(从低位数起)为1.
基本的操作:
#define WORD 32
#define SHIFT 5 移动5个位,左移则相当于乘以32,右移相当于除以32取整
#define MASK 0x1F //16进制下的31
#define N 10000000
int bitmap[1 + N / WORD];
/*
* 置位函数——用"|"操作符,i&MASK相当于mod操作
* m mod n 运算,当n = 2的X次幂的时候,m mod n = m&(n-1)
*/
void set(int i) {
bitmap[i >> SHIFT] |= (1 << (i & MASK));
}
/* 清除位操作,用&~操作符 */
void clear(int i) {
bitmap[i >> SHIFT] &= ~(1 << (i & MASK));
}
/* 测试位操作用&操作符 */
int test(int i) {
return bitmap[i >> SHIFT] & (1 << (i & MASK));
}
int main(void) {
FILE *in = fopen("in.txt", "r");
FILE *out = fopen("out.txt", "w");
if (in == NULL || out == NULL) {
exit(-1);
}
int i = 0;
int m;
for (i = 0; i < N; i++) {
clear(i);
}
while (!feof(in)) {
fscanf(in, "%d", &m);
printf("%d/n", m);
set(m);
}
printf("abnother");
for (i = 0; i < N; i++) {
if (test(i)) {
printf("%d/n", i);
fprintf(out, "%d/n", i);
}
}
fclose(in);
fclose(out);
return EXIT_SUCCESS;
}