【题目】
有很多不重复的整数, 其中最大值不超过 40 亿, 最小值是 0. 要求判断某个指定的整数, 是否在这个集合中.
最快的算法:
位图算法。
使用两个字节,表示16个数(0-15)的状态(1表示有,0表示没有)。
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
如果存入的数据为【12,9,5,3,1】,则存储为:
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 |
代码:
#include <iostream>
#include <windows.h>
using namespace std;
void init(char* data, int len)
{
//假设:存入的数据为能被3整除的数
unsigned int n = len * 8; //一共有n个数据
for (unsigned int i = 0;i < n;i++)
{
if (i % 3 == 0)
{
char* p = data + i / 8; //求出i所在字节
*p = *p | (1 << (i % 8));
}
}
}
bool check(char* data, int len, int value)
{
//定位到指定的字节
char* p = data+value / 8;
//判断这个字节中指定的位是否为1
bool ret = *p & (1 << (value % 8));
return ret;
}
int main(void)
{
unsigned int n = 4000000; //位数
int len = n / 8 + 1; //考虑只有7位的情况
char* data = new char[len]; //分配一块内存空间
memset(data, 0, len); //清零
//装载数据集合(只需要装载一次)
init(data, len);
while (1)
{
cout << "请输入要检测的数:[输入-1结束]" << endl;
int value;
cin >> value;
if (value == -1)
{
break;
}
if (check(data, len, value))
{
cout << value << "在数据集合中" << endl;
}
else
{
cout << value << "不在数据集合中" << endl;
}
}
delete data;
system("pause");
return 0;
}
补充:
位或 |
0 | 0 0
0 | 1 1
1 | 0 1
1 | 1 1
对应位, 都是 0, 结果才是 0 只要有一位是 1, 结果就是 1
0 | x x
// 00001000
// 00000011
// 00001011
cout << " 8 | 3 = " << (8 | 3) << endl;
位与 &
0 & 0 0
0 & 1 0
1 & 0 0
1 & 1 1
对应位, 都是 1, 结果才是 1
// 00001000
// 00000011
// 00000000
cout << " 8 & 3 = " << (8 & 3) << endl;
<< 左移
右边的位用 0 来填充, 移动超出左边边界的位则直接抛弃。 向左移动 n 个位置,就等同于乘
以 2 的 n 次方
只适用于: int/short/long/char 等定点数类型(float/double 浮点数类型不能使用)
// 00001000
// 01000000
cout << (8<<3) << endl; //8*2*2*2 = 64