[C++ 系列] 84. 位图的概念及应用

1. 一个面试题

话说海量数据面试题火爆于各类面试环节中,动不动就 100G 数据查一个数是不是存在啥的…其实考的就是位图这个知识点,也是哈希的应用。

  1. 面试题
    给 40 亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这 40 亿个数中。【腾讯】
    1. 遍历,时间复杂度 O ( N ) O(N)
    2. 外部排序 ( O ( N l o g N ) ) (O(NlogN)) ,利用多路归并、二分查找: O ( l o g N ) O(logN)
    3. 位图解决:数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为 1,代表存在,为 0 代表不存在。比如:在这里插入图片描述

2. 位图概念

所谓位图,就是用每一位来存放某种状态,适用于大规模数据,但数据状态又不是很多的情况。通常是用来判断某个数据存不存在的。

一个字节可以存放八种状态,值得注意的是,如果我们需要存放一个 int 范围的所有数据,即 4G 个数据,我们仅需要 4G / 8 = 512M 大小的空间就够了。

3. 位图的模拟实现

在此没有 operator[ ],在系统的底层实现中是重载过的。

参见代码如下:

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

class bitmap
{
	vector<int> m_bit;
	
	// 位图的capacity
	size_t m_bitcount;
public:
	bitmap(size_t bitc) :
		m_bit(bitc / 32 + 1),
		m_bitcount(bitc)
	{}

	// 位图对应位置置1
	void set(size_t pos)
	{
		if (pos > m_bitcount)
		{
			return;
		}
		// 下标
		size_t index = pos / 32;
		// 左移位数
		size_t bits = pos % 32;

		// 不影响其它位情况下:
		// |= 1 置1  
		// &= ~1 置0  
		// ^= 1 状态改变
		m_bit[index] |= 1u << bits;
	}

	void reset(size_t pos)
	{
		if (pos > m_bitcount)
		{
			return;
		}

		size_t index = pos / 32;
		size_t bits = pos % 32;

		m_bit[index] &= ~(1u << bits);
	}

	bool get(size_t pos)
	{
		if (pos > m_bitcount)
		{
			return;
		}

		size_t index = pos / 32;
		size_t bits = pos % 32;

		return m_bit[index] >> bits & 1;
	}

	size_t size()
	{
		return m_bitcount;
	}

	size_t count()
	{
		// 数一的第四种方法,前三种方法在剑指-offer注意查看
		// 对应数字的1有多少个
		const char * pCount = "\0\1\1\2\1\2\2\3\1\2\2\3\2\3\3\4";
							//  0 1 2 3 4 5 6 7 8 9 a b c d e f

		size_t size = m_bit.size();
		size_t count = 0;
		size_t i;
		for (i = 0; i < size; i++)
		{
			int value = m_bit[i];
			int j;
			for (j = 0; j < sizeof(m_bit[0]); j++, value >>= 8)
			{
				// char占1个字节只能取出最后的8位
				char c = value;
				
				// 与00001111进行与运算,得到对应的:0123456789abcdef
				// 然后直接在对应位置上取出来1的位数即可
				// 然后对c右移四位在进行如上操作
				count += pCount[c & 0x0f];
				c >> 4;
				count += pCount[c & 0x0f];
				// 至此,后8位1的个数已经统计完毕,再将数字右移八位进行统计
				// 这需要进行 32 / 4 = 8 步的操作,并不快,最快的方法三是log 32 = 5的方法
				
				// 在此统计1时也可以根据数据进来一个统计一次,设置一个计数即可
				// 但是这种方法每次需要判断,当数据量大时,开销会很夸张
			}
		}
	}
};

4. 位图的应用

  1. 快速查找某个数据是否在一个集合中
  2. 排序
  3. 求两个集合的交集、并集等
  4. 操作系统中磁盘块标记
发布了359 篇原创文章 · 获赞 259 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/yl_puyu/article/details/104889866
今日推荐