哈希的应用——位图

文章介绍了如何使用位图数据结构解决海量整数查找的问题,展示了std库中的bitset用法,并提供了自定义位图类的实现,包括set、reset和test函数的详细代码。
摘要由CSDN通过智能技术生成

目录

一,引入

二,问题引入

三,std里的位图

四,位图的实现

1.成员

2.初始化

3.set成员函数(如果一个数出现便将对应的比特位改为1)

4.reset函数(如果一个数出现便将对应的比特位改为0)

5.test函数(检验数字存不存在)

5.检验

详细代码:


一,引入

首先介绍一下位图,位图是啥呢?所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用 来判断某个数据存不存在的。首先在这里解释一下:

1.位图的位是比特位。

2.位图面对的海量数据是整形的海量数据。

所以这里便暴露了位图的一个局限性,位图只能用来查找整形数据。

二,问题引入

第一道题是一道面试题,题目如下:

1. 面试题

40 亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在
40 亿个数中。【腾讯】
  这道题便是要使用位图来解决的一道经典题目。首先40亿个数字绝对算得上是海量数据,其次我们要查找的是整数。使用位图的两个条件都有了,所以这道题可以使用位图。
  那我们为什么不使用其它的数据结构来解决这道题呢?比如红黑树,哈希表,AVL树这些高效率的数据结构?其实主要在一个点: 海量数据光是存储都能把内存折腾光了。所以使用这些数据结构是万万不能的。所以我们只能用位图了。

三,std里的位图

传送门:std里的bitset

在手撕位图之前,先来把std库里的位图的样子看一遍,如下图:

    首先我们可以知道位图叫:bitset,其次位图是一个非类型参数的模板类(参数的类型已确定是一个size_t类型)。

    其次我们再来看看这个N是啥意思:

通过这个解释我们知道了,N是number of bits。所以N就代表比特位的数量。好了现在我们把位图的大概样子摸清楚了,现在就来实现一下吧。

四,位图的实现

1.成员

在实现这个位图时,为了简单一点。我的成员就直接采用vector<int>了。定义如下:

template<size_t N>//非类型模板参数

class bitset
{
     
 private:
	vector<char>bits;
};

2.初始化

   一个类要初始化,我们使用的肯定是构造函数。但是要开多少个比特位,表示的数的范围是多少这些都是要我们传进去的。所以我们在初始化时一定要使用带有参数的构造函数,并且这个参数就是我们的N(N是非类型模板参数) 。在初始化时便可以复用vector里面的resize()函数,但是resize()函数里面传的个数代表的整型的个数,而我们要的个数确是比特位的个数所以在这里便要处理一下:N/32+1(表示要开的整型的个数)代码如下:

bitset()
{
   bits.resize(N /32 + 1,0);//开空间,开的是整型值的空间,并且要初始化为0
}

3.set成员函数(如果一个数出现便将对应的比特位改为1)

   首先我们要明确的是每一个比特位对应着一个数。那我们的映射关系该如何处理呢?

其实也简单,要分两步:

1.找到你所在的整数下标,int i = x/32。

2.找到你在这个整数的第几个比特位,int j = x%32。

比如我开了100个比特位。那我对应的整数的个数便是n = 100/32+1,一共有4个整数0。

那如果我插入一个40,40映射的位置是多少呢?

1.整数下标:i = 40/32 ==1。

2.所在比特位:j = 40%32 == 8。

    然后我们要做的便是将40映射的位置的值改为1,其它位置不变。如何搞呢?其实也简单,就是将所在的整数或等(|=)(i<<j)。如下代码所示:

void set(size_t x)//把对应整数的比特位置为1
{
	int i = x / 32;//表示在第几个整数。i表示下标
	int j = x % 32;//对应的整数的比特位。

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

4.reset函数(如果一个数出现便将对应的比特位改为0)

在写完set函数 以后,我们已经知道了如何找到映射的比特位了。所以在这里要做的便是将映射的比特位上的数字改为0,其它位置不变。如何操作呢,其实也简单。如下:

void reset(size_t x)//把对应整数的比特位置为0
{
   int i = x / 32;//表示在第几个整数。i表示下标
   int j = x % 32;//对应的整数的比特位。

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

5.test函数(检验数字存不存在)

 如何检验一个数字存不存在呢?还是一样,先找到这个数字的映射比特位。然后让这再让这个数字映射的比特位所在的整数做&(1<<j)的操作。如在就得到一个非零数,如不在便得到一个0。代码如下:

bool test(size_t x)//检查是否出现
{
  int i = x / 32;//表示在第几个整数。i表示下标
  int j = x % 32;//对应的整数的比特位。

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

5.检验

现在我们得想一想了,我们要开多大的数据呢?多少个比特位呢?是40亿个比特位吗?

1.首先我们要明确:不是40亿个比特位。而是42亿九千万....个比特位(无符号整型的最大值个)。

2.那我们该如何开呢?用INT_MAX行吗?不行因为INT_MAX是有符号整型的最大值。那2*INT_MAX呢?还是不行,因为溢出然后被截断。

3.我们应该使用的是-1或者0xffffffff,来开空间。因为我们在类里面使用到的是size_t类型的参数,传入-1会被识别为最大的无符号整型值。

验证如下:

详细代码:

template<size_t N>//非类型模板参数

	class bitset
	{
	public:
		bitset()
		{
			bits.resize(N /32 + 1,0);//开空间,开的是整型值的空间,并且要初始化为0
		}

		void set(size_t x)//把对应整数的比特位置为1
		{
			int i = x / 32;//表示在第几个整数。i表示下标
			int j = x % 32;//对应的整数的比特位。

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

		void reset(size_t x)//把对应整数的比特位置为0
		{
			int i = x / 32;//表示在第几个整数。i表示下标
			int j = x % 32;//对应的整数的比特位。

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

		bool test(size_t x)//检查是否出现
		{
			int i = x / 32;//表示在第几个整数。i表示下标
			int j = x % 32;//对应的整数的比特位。

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

	private:
		vector<char>bits;
	};

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值