ps:左移位并不是向左移动位,而是低数据位向高数据位挪动

位图(主要接口,set(size_t)标识、reset(size_t)取消、test(size_t) 查看

给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在 这40亿个数中。【腾讯】

这么多的数据根本无法在内存中存储,所以,将他们放进set,map中这是不切实际的。

遍历一遍数据,也许可以,但是如果再来一个查找值,难道还要再遍历一遍吗?再来再遍历?

注意审题:

  1. 我们会发现,我们并不需要将数据存储起来,我们只是需要知道,他是否存在于这40亿个数中,所以我们只需要判断40个亿中有没有该数。
  2. size_t类型的数据范围是0~40亿多,如果普通的哈希创建size_t类型的数组来标识下标对应的是否重载,我们需要4*40亿个字节的空间=>16个G的内存,机器不说能不能存下,就是是64位存下了,那么我们的操作系统怎么办???

构造位图bit_set

成员变量:

template <size_t N>
class bitset
{	
//........
private:
	vector<char> _map;//图的意思
}

注意我们其实只是想知道是否存在,标识40亿个数据罢了,并不是真的要存储40亿个数据,所以我们使用计算机存储单位最小的类型比特位来存储数据,应为size_t类型范围的最小是0,最大是42亿多,所以这道题我们需要直接创建42亿个比特位来存储,数组下标的位置映射元素是否存在该数,1标识存在,0标识不存在。注意我们无法直接创建比特类型的数组,我们需要创建其他类型,然后位处理比特位。所以我们创建char类型的数组来映射比特数组。哈希的应用->位图-LMLPHP

哈希的应用->位图-LMLPHP

所以我们的数组只需要开char[40亿/8]大小的数组(一个char可以标识8个数据)

这样我们存在的问题,就不需要考虑类型了,只需要考虑个数问题。

构造函数代码

bitset()
{
	_bits.resize(N/8+1,0);//将所有位初始化为0
}

标识数据位set(size_t)

我们需要把这40亿个size_t 数据标识到位图中。

例:我们将40亿中的26数据标识到位图中

2步走

  1. 寻找需要标识的位置
  2. 标识比特位为1

寻找数据标识位: 

哈希的应用->位图-LMLPHP

我们确定了26数据存储在map[3]的第二个比特位,

修改标记数据位为1:

我们现在需要把这个位置的bite位给标记为1,该如何呢?

我们需要对该位置继续位操作,

使用位运算符号|对图中该位置改为1,并且其他位置不动,所以我们需要一个数据同位为1,其他位为0,与图的该char类型元素进行逻辑或运算, 

哈希的应用->位图-LMLPHP 

哈希的应用->位图-LMLPHP这样,该位无论是什么在或上1都被标识为1,其他位或上0先前是什么,现在依旧是什么

这样我们的数据就标识完毕。

代码:

void bit_set::set(size_t x)
{
	int loction = x / 8;
	int bitloction = x % 8;
	char  flag = 1;
	flag = flag << bitloction;
	_bits[loction] |= flag;
}

取消标识位( reset() )

假设我们把刚刚set的数据26   reset()取消掉

2步走

  1. 寻找需要标识的位置
  2. 标识比特位为0

寻找数据标识位

首先也是寻找位和我们之前set()操作是一样的,

数据/8得:map得下标元素,

数据%8得:标识该元素得比特位

所以我们跳过寻找逻辑。

修改标记数据位为0:

修改得逻辑其实和之前是一样的,但是我们现在需要的是将该位置标识为0,其他位置不变,

需要对数据标记位逻辑与0,保证该其位置标识改变为0(不论操作标识位是什么都改为0)

需要对其它位置逻辑与1,保证其他位置标识不受到改变为0(不论其他标识位是什么,都不能改变)

哈希的应用->位图-LMLPHP

 然后也是对数据1进行位运算操作~直接上图

哈希的应用->位图-LMLPHP

这样,该位无论是什么在与上0都被标识为0,其他位与上1先前是什么,现在依旧是什么

这样我们的数据就取消标识。

代码:

void reset(size_t x)
{
	int loction = x / 8;
	int bitloction = x % 8;
	char flag = 1;
	flag = flag << bitloction;
    flag=(~flag);
	_bits[loction] &= flag;
}

查看数据是否已标识 test()

现在我们并不知道该26数据是否在位图中。

2步走

  1. 寻找需要标识的位置
  2. 查看标识位

寻找数据标识位

首先也是寻找位和我们之前set()操作是一样的,

数据/8得:map得下标元素,

数据%8得:标识该元素得比特位

所以我们跳过寻找逻辑。

查看该位置是否为被标识

获取到了,我们只需要找到该位数据存不存在并不怎么样,所以我们只需要真假,不关心是具体数据是什么。

所以我们将该char元素其他的比特位置0,其用比特位1与其按位与,检查其是否存在。

所以我们需要map[3]:10X0 1101  -> 00X0 0000 的数据,获取辅助数据的方法与set()一样。

哈希的应用->位图-LMLPHP

注意,test实现不可改变map[3]的值,所以保存到一个变量,或者按位与数据返回

代码:

bool test(size_t x)
{
	int loction = x / 8;
	int bitloction = x % 8;
	char flag = 1;
	flag = flag << bitloction;
	return _bits[loction] & flag;
//  bool tmp=_bits[loction] & flag;
//  return tmp;
}

回到上题:给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在 这40亿个数中。【腾讯】

1、bitset<-1> _map; 范围开size_t的取值范围 42亿多个比特位

2、_map.set(40亿size_t);  将40亿个数据映射到位图中

3、cout<<_map.test(查找的数据)  查看数据是否在位图中,1就是在,0就是不在 

完成


完整代码:

template <size_t N>
class bitset
{
public:
	bitset()
	{
		_bits.resize(N / 8 + 1, 0);
	}
	void set(size_t x)
	{
		int loction = x / 8;
		int bitloction = x % 8;
		char  flag = 1;
		flag = flag << bitloction;
		_bits[loction] |= flag;
	}

	void reset(size_t x)
	{
		int loction = x / 8;
		int bitloction = x % 8;
		char flag = 1;
		flag = flag << bitloction;
		_bits[loction] &= (~flag);
	}

	bool test(size_t x)
	{
		int loction = x / 8;
		int bitloction = x % 8;
		char flag = 1;
		flag = flag << bitloction;
		return _bits[loction] & flag;
	}

private:
	vector<char> _bits;
};
07-12 01:37