C++ 哈希的应用--位图

目录

位图的引入

位图概念

位图代码

位图应用


位图的引入


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

先考虑内存的问题:40亿个整数, 每个整数4字节,换算大约16G的空间,寻常的查找算法都是不可能完成的。因此引入位图

位图概念


        所谓位图,就是用每一位比特位来存放某种状态,适用于海量数据,数据无重复的场景通常是用来判断某个数据存不存在的 ,位图实际上也是使用哈希思想,只不过是哈希的变形

        通过这种同样是哈希的思想,就可以极大的节省空间,无符号整形范围在0~2的32次方-1,因此我们可以开2的32次方个比特位,换算下来只有512M的大小,通过直接映射就能够判断。

我们无法开这么大的数组,但我们采用的是bit位的标记,即值是几,就把第几个位标记成1。

那么如何去找呢?实际上我们把数组的元素类型规定为char(int也可以),这样就可以通过如下的方式去找任意一个数:x

  • x映射的值,在第几个char对象上:x/8

  • x映射的值,在这个char对象的第几个比特位:x%8

比特位顺序问题:

事实上,比特位的顺序与机器的大小端无关,仍是和地址一样从右到左:

image-20230303193809130

 1字节 = 8bit,代表的整数就是0~7,比特位为1则表示该数存在

位图代码


对于位图的功能,要有插入,删除,检测在不在三个功能,如果这样赋值:

  • size_t i = x / 8;
  • size_t j = x % 8;

插入就可以这样:_bits[i] |= (1 << j);

删除就可以这样:_bits[i] &= (~(1 << j));

测试这个值在不在就可以这样:return _bits[i] & (1 << j);

此外,一开始同样需要开辟指定大小的空间,因此构造函数中要写出来。

template<size_t N> // 非类型模板参数:用一个整形作为模板的一个参数,在模板中可以当常量使用
class bitset
{
public:
    bitset()
	{
		_bits.resize(N / 8 + 1, 0);
	}

    void set(size_t x)//标记
    {
        size_t i = x / 8;
        size_t j = x % 8;

        _bits[i] |= (1 << j);

    }
    void reset(size_t x)//清除
    {
        _bits[i] &= (~(1 << j));
    }

    bool test(size_t x)//测试这个值在不在
    {
        size_t i = x / 8;
        size_t j = x % 8;

        return _bits[i] & (1 << j);
    }
private:
    vector<char> _bits;
};

下面是int类型的代码

class bitset {
public:
	bitset(size_t N) {
		_bits.resize(N / 32 + 1, 0);
		_num = 0;
	}

	void set(size_t x) {
		size_t index = x / 32;  // 算出x映射的位在第几个整形
		size_t pos = x % 32;  // 算出x在这个整形中第几个位置(bit位)

		_bits[index] |= (1 << pos); // 第pos个位置成1
		// 左移、右移这里的左右不是方向
		// 左移是向高位移
		// 右移是向低位移

		++_num;
	}

	void reset(size_t x) {
		size_t index = x / 32;  // 算出x映射的位在第几个整形
		size_t pos = x % 32;  // 算出x在这个整形中第几个位置(bit位)

		_bits[index] &= ~(1 << pos); // 第pos个位置成0

		--_num;
	}

	// 判断x在不在
	bool test(size_t x) {
		size_t index = x / 32;  // 算出x映射的位在第几个整形
		size_t pos = x % 32;  // 算出x在这个整形中第几个位置(bit位)

		return _bits[index] & (1 << pos);

	}

private:
	std::vector<int> _bits; // 一个int4个字节,一个字节8个bit位
	size_t _num;
};

测试

void test_bitset() {

	bitset bs(100);
	bs.set(99);
	bs.set(98);
	bs.set(97);
	bs.set(96);
	for (size_t i = 0; i < 100; ++i) {
		printf("[%d]:%d\n", i, bs.test(i));
	}
	cout << bs.test(96);

	//bitset bs1(-1);//无符号最大数直接传-1,会转成最大数量
	//bitset bs2(pow(2,32));
	//bitset bs3(0xffffffff);
}

库中也有位图结构 bitset ,也可以直接使用库中的位图。

#include <bitset>

位图应用

一. 给定100亿个整数,设计算法找到只出现一次的整数?

对于此整数有三种状态:

  1. 出现0次
  2. 出现1次
  3. 出现一次以上

因此,我们可以通过两个比特位来标记:00代表出现0次;01代表出现1次,其他的代表出现一次以上。(两个标记位可以表示从1到3),此方式虽然可行,但需要第上述代码做出很大变动。那么我们可以采用另一种方式代表两个比特位,即以开两个位图的方式,每个位图的一个比特位组合成两个比特位进行标记

    template<size_t N>
    class solution {
	public:
		void set(size_t x) {
			if (_bs1.test(x) == false && _bs2.test(x) == false) {   // 00
				_bs2.set(x);  //   01
			}
			else if (_bs1.test(x) == false && _bs2.test(x) == true) {  // 01
				_bs1.set(x);  
				_bs2.reset(x);  //   10
			}
		}

        void PrintOnce()
	    {
		    for (size_t i = 0; i < N; ++i)
		    {
			    if (!_bs1.test(i) && _bs2.test(i))
			    {
			    	cout << i << endl;
		    	}
	    	}
		    cout << endl;
	    }

	private:
		bitset<N> _bs1;
	    bitset<N> _bs2;

	};

测试

	void test_solution()
	{
		solution<100> tbs;
		int a[] = { 3, 5, 6, 7, 8, 9 ,33, 55, 67, 3,3,3,5,9,33 };
		for (auto e : a)
		{
			tbs.set(e);
		}
		tbs.PrintOnce();
	}

二. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?

方案1: 将其中一个文件1的整数映射到一个位图中,读取另外一个文件2中的整数,判断在在不在位图,在就是交集。消耗5OOM内存
方案2: 将文件1的整数映射到位图1中,将文件2的整数映射到位图2中,然后将两个位图中的数按位与。与之后为1的位就是交集。消耗内存1G。

三. 位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数

本题找的不超过2次的,也就是要找出现1次和2次的
本题还是用两个位表示一个数,分为出现0次00表示,出现1次的01表示,出现2次的10表示出现3次及3次以上的用11表示

总结一下位图的应用:

  1. 快速查找某个数据是否在一个集合中
  2. 排序 + 去重
  3. 求两个集合的交集、并集等
  4. 操作系统中磁盘块标记

        位图有如下优点:1.节省空间。2.快。但相对的,位图同样有缺点存在:1. 一般要求范围相对集中,如果范围特别分散,空间消耗就会上升。2. 只能针对整形

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值