【数据结构】—— 哈希的应用之位图

位图的概念

位图的原理
  • 位图是通过将数组下标与应用中的一些值关联映射,数组中该下标所指定的位置上的元素可以用来标识应用中值的情况(是否存在或者数目 或者计数等),位图数组中每个元素在内存中占用1位,所以可以节省存储空间。
  • 位图是一种非常简洁快速的数据结构,它能同时使存储空间和速度最优化。数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。比如:
    【数据结构】—— 哈希的应用之位图_第1张图片

位图相关操作

  • 设计一个位图,我们这里不使用一个数组自己开辟空间,而是使用vector让来开辟一段连续的空间,我们只用考虑增删查改操作。
class BitMap
{
public:
	BitMap(size_t range)
	{
		//右移5位相当于除以32,加1是因为小于32的数字如果与32相除则得到0
		_bitTable.resize((range >> 5) + 1);
	}

private:
	vector _bitTable;
};
  • 位图的设置,即读入数据
	void SetBit(size_t x)
	{
		size_t index = x >> 5;//右移5个比特位就是除以32
		size_t num = x % 32;

		_bitTable[index] |= (1 << num);
	}

【数据结构】—— 哈希的应用之位图_第2张图片

  • 位图元素的移除
  • 思想也是,很简单了,找到x的对应位置,和之前置1的方法一样,之后通过位运算&,将对应位置置0,而其他位置不变即可。
	void RemoveBit(size_t x)
	{
		size_t index = x >> 5;
		size_t num = x % 32;

		//~(1 << num),对应位为0,其他位为1,相与之后对应位置0,其余位不变
		_bitTable[index] &= ~(1 << num);
	}
  • 查找元素是否在位图中
	bool TestBit(size_t x)
	{
		size_t index = x >> 5;
		size_t num = x % 32;

		//若该位为1与1相与之后就是1则存在,若该位为0,与1相与后就是0,则不存在
		return _bitTable[index] & (1 << num);
	}

位图法的缺点

  • 可读性差
  • 位图存储的元素个数虽然比一般做法多,但是存储的元素大小受限于存储空间的大小。位图存储性质:存储的元素个数等于元素的最大值。比如, 1K 字节内存,能存储 8K 个值大小上限为 8K 的元素。(元素值上限为 8K ,这个局限性很大!)比如,要存储值为 65535 的数,就必须要 65535/8=8K 字节的内存。要就导致了位图法根本不适合存 unsigned int 类型的数(大约需要 2^32/8=5 亿字节的内存)。
  • 位图对有符号类型数据的存储效率较低,需要 2 位来表示一个有符号元素。这会让位图能存储的元素个数,元素值大小上限减半。 比如 8K 字节内存空间存储 short 类型数据只能存 8K*4=32K 个,元素值大小范围为 -32K~32K 。

位图法的应用

  • 1、给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中。
    首先,将这40亿个数字存储到BitMap中,然后对于给出的数,判断是否在BitMap中即可。
  • 2、使用位图法判断整形数组是否存在重复
    遍历数组,一个一个放入BitMap,并且检查其是否在BitMap中出现过,如果没出现放入,否则即为重复的元素。
  • 3、使用位图法进行整形数组排序
    首先遍历数组,得到数组的最大最小值,然后根据这个最大最小值来缩小BitMap的范围。这里需要注意对于int的负数,都要转化为unsigned int来处理,而且取位的时候,数字要减去最小值。
  • 4、在2.5亿个整数中找出不重复的整数,注,内存不足以容纳这2.5亿个整数
    参考的一个方法是:采用2-BitMap(每个数分配2bit,00表示不存在,01表示出现一次,10表示多次,11无意义)。另一个方法:这里可以使用两个普通的BitMap,即第一个BitMap存储的是整数是否出现,如果再次出现,则在第二个BitMap中设置即可。这样的话,就可以使用简单的1- BitMap了。

位图源码

#pragma once

#include 
#include 
using namespace std;

class BitMap
{
public:
	BitMap(size_t range)
	{
		//右移5位相当于除以32,加1是因为小于32的数字如果与32相除则得到0
		_bitTable.resize((range >> 5) + 1);
	}

	void SetBit(size_t x)
	{
		size_t index = x >> 5;//右移5个比特位就是除以32
		size_t num = x % 32;

		_bitTable[index] |= (1 << num);
	}

	void RemoveBit(size_t x)
	{
		size_t index = x >> 5;
		size_t num = x % 32;

		//~(1 << num),对应位为0,其他位为1,相与之后对应位置0,其余位不变
		_bitTable[index] &= ~(1 << num);
	}

	bool TestBit(size_t x)
	{
		size_t index = x >> 5;
		size_t num = x % 32;

		//若该位为1与1相与之后就是1则存在,若该位为0,与1相与后就是0,则不存在
		return _bitTable[index] & (1 << num);
	}

private:
	vector _bitTable;//一个int大小就是4个字节,32个比特位
};

你可能感兴趣的:(数据结构学习代码)