当前位置: 代码网 > it编程>软件设计>数据结构 > 哈希表——位图

哈希表——位图

2024年07月28日 数据结构 我要评论
哈希表——位图

之前我们已经了解了哈希表的底层实现,今天我们来了解一下哈希表思想的衍生产物——位图

基本概念

在了解位图之前,我们先来了解一些简单的概念。
我们都知道,计算机的数据和指令都是二进制的:
在这里插入图片描述二进制里面只有0,1两个数字,这就意味着这两个数字可以表示两个不同的状态,位图的基础也是基于这一点。

所以,我们利用这一点。假设我们现在有一堆海量的数据,我可以通过某种方式让数字对应到相应的二进制的数位上,同时规定0表示该数字不存在,1表示存在。这本质上也是一种哈希的思想。

在这里插入图片描述

一道面试题

如果我们硬来,先将这40亿个数存起来,然后二分查找。这无疑是不行的。因为,光是把数据存起来就要用掉16gb的内存,而且要求空间连续。

在这里插入图片描述
但是这时候我们用位图,让位图的每一位对应一个数字,1表示存在,0表示不存在。这样可以大大降低内存的消耗:
在这里插入图片描述
大概我们想要是这样的:
在这里插入图片描述我们所有的位是用来映射存在关系的。

位图实现

我们先来搭搭架子:

#pragma once
#include<iostream>
#include<vector>
using namespace std;

namespace my_bitmap
{
	//n是需要多少bit位
	template<size_t n>
	class bitmap
	{
	public:

	private:
		vector<int> _bmp;
	};
}

现在我们来计算需要多少内存,然后通过构造函数,开出来相应的空间:

#pragma once
#include<iostream>
#include<vector>
using namespace std;

namespace my_bitmap
{
	//n是需要多少bit位
	template<size_t n>
	class bitmap
	{
	public:
		bitmap()
		{
			_bmp.resize(n / 32 + 1, 0); //开出相对应的空间,初始化所有位均为0
		}

	private:
		vector <int> _bmp;
	};
}

设置存在或不存在

现在我们有一个数假设为241,我们要把它设置到位图中,将相应的位设置为1。

因为我们以32位为一组,我们把241 / 32 ,可以得到241被分到了哪一个组,然后241 % 32 的到的是241在第几组的第几位:

在这里插入图片描述
现在我们找到了相应的位置,现在我们要将这个位从0变成1,表示241已经存在,但是我们不能影响其他位的状态,这个时候我们要想起来我们的位运算:
我们来实现一下:
在这里插入图片描述

		bool set(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;

			return _bmp[i] |= (1 << j);
		}

好了,我们现在完成了设置,但是现在我想把它又变为0,表示241又不存在了,该怎么办呢?

其实大同小异:
在这里插入图片描述

		bool unset(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;

			return _bmp[i] &= (~(1 << j));
		}

检查存在

检查存在和设置存在其实都差不多,只不过把或换成了与:

		bool check(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;

			return _bmp[i] & (1 << j);
		}

我们来测试一下:

#include"bitmap.h"

int main()
{
	my_bitmap::bitmap<10000> _bt;

	_bt.set(241);
	cout << "是否存在:" << _bt.check(241) << endl;
	_bt.unset(241);
	cout << "是否存在:" << _bt.check(241) << endl;

	return 0;
}

在这里插入图片描述

解决一开始的问题

现在我们封装好了一个位图,但是我们只是解决了数据的储存,我们还没有解决如何只找出出现一次的数。

其实我们发现,这里面有三种状态:一次都没有出现的,出现一次,出现一次及其以上

我们发现,这也表示状态,这样我们可以用我们上面位图再封装一个位图,这个位图用来寻找只出现了一次的数:

要表示三种状态,我们需要两个位图:
在这里插入图片描述

	//n是需要多少bit位
	template<size_t n>
	class twobit_map
	{
	public:
		void set(size_t x)
		{
			if (_bt1.check(x) == false && _bt2.check(x) == false)
			{
				_bt2.set(x);
			}
			else if (_bt1.check(x) == false && _bt2.check(x) == true)
			{
				_bt1.set(x);
				_bt2.unset(x);
			}
		 }

		void print()
		{
			for (size_t i = 0; i < n; i++)
			{
				if (_bt1.check(i) == false && _bt2.check(i) == true)
					cout << "出现一次:" << i << endl;
			}
		}
	private:
		bitmap<n> _bt1;
		bitmap<n> _bt2;
	};

测试一下:

	my_bitmap::twobit_map<241> _bt;

	int a[] = { 12,34,45,67,12,90,90,45 };

	for (auto e : a)
	{
		_bt.set(e);
	}

	_bt.print();

在这里插入图片描述我们依次类推,还可以找出出现了一次和两次的数:

	template<size_t n>
	class twobit_map
	{
	public:
		void set(size_t x)
		{
			if (_bt1.check(x) == false && _bt2.check(x) == false)
			{
				_bt2.set(x);
			}
			else if (_bt1.check(x) == false && _bt2.check(x) == true)
			{
				_bt1.set(x);
				_bt2.unset(x);
			}
			else if (_bt1.check(x) == true && _bt2.check(x) == false)
			{
				_bt2.set(x);
			}
		 }

		void print()
		{
			for (size_t i = 0; i < n; i++)
			{
				if (_bt1.check(i) == false && _bt2.check(i) == true)
					cout << "出现一次:" << i << endl;
				else if (_bt1.check(i) == true && _bt2.check(i) == false)
					cout << "出现两次" << i << endl;
			}
		}
	private:
		bitmap<n> _bt1;
		bitmap<n> _bt2;
	};

这样,我们可以灵活运用位图完成数据的查找,对于40亿的数据我们只需要开足够大的空间即可:

my_bitmap::twobit_map<0xffffff> _bt;

或者

my_bitmap::twobit_map<-1> _bt;

不过这个方法的保证自己写的代码中都使用的是无符号数,不然会有一点问题。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com