C++_BitSet位图(大数处理_C++实现+位图面试题分析+变形)

1.位图概念

位图概念:所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景通常是用来判断某个数据存不存在的。

2.面试题引出位图

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

解决思路:
首先分析40亿个不重复的整数加载到内存需要的空间大小
1G=10^9=10亿字节
一个整数=4个字节
4G=40亿字节=10亿整数
所以40亿整数大概需要16G的空间,一般的查找思路如排序二分查找在内存上肯定是行不通。

因为我们只需要判断这个数字是否在40亿个数中即可,所以为了节约空间,可以用一个1个比特位来表示这个数字在不在。

一个int有32个比特位,之前一个int对应一个整数,现在一个int可以标记32个整数,节约了空间。所用空间大概为16G/32=500M。方案可行

C++实现简单的位图

#pragma once

#include<vector>
#include<cassert>

using namespace std;

namespace NUC
{
	template<size_t Size>//Size表示开几位
	class BitSet
	{
	public:
		BitSet() {
			_bits.resize((Size / (4 * 8)) + 1, 0);//多开一个整数,保证所有整数都可以映射到
		}

		void Set(size_t x)//把x映射的位标记为1
		{
			assert(x < Size);
			//先计算x在那个整数上
			size_t index_int = x / 32;
			//计算x在哪一位
			size_t index_bit = x % 32;
			
			_bits[index_int] |= (1 << index_bit);
		}

		void ReSet(size_t x)//把x映射的位标记为0
		{
			assert(x < Size);
			//先计算x在那个整数上
			size_t index_int = x / 32;
			//计算x在哪一位
			size_t index_bit = x % 32;

			_bits[index_int] &= (~(1 << index_bit));
		}

		bool Find(size_t x)//查找x是否在位图上,true为存在
		{
			assert(x < Size);
			//先计算x在那个整数上
			size_t index_int = x / 32;
			//计算x在哪一位
			size_t index_bit = x % 32;

			//判断第index_bit为是否为1
			return ((_bits[index_int] >> index_bit) & 1) == 1;
		}
	private:
		vector<int>_bits;
	};
}

测试

#include"BitSet.h"
#include<iostream>

using namespace std;

void Test()
{
	NUC::BitSet<-1>bits;//-1提升到size_t大小为2^32-1大约为42亿
	long long int NumTest = 18734;
	bits.Set(NumTest);
	if (bits.Find(NumTest))
	{
		cout << "YesSet" << endl;
	}
	bits.ReSet(NumTest);
	if (bits.Find(NumTest) == false)
	{
		cout << "YesyReSet" << endl;
	}
}

int main()
{
	Test();
	return 0;
}

输出结果:
在这里插入图片描述
所占内存大概为500MB
在这里插入图片描述

3.位图的变形题

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

方案一:
把第一个文件的整数映射到一个位图中,再读取另一个文件的所有整数,判断在不在位图中,在就是交集中的数,否则不在。
注意:100亿个整数肯定存在重复,整数最大为2^32个大小为500MB

方案二:
把第一个文件的整数映射到一个位图1中,再读取另一个文件的所有整数映射到一个位图2中,
在对两个位图进行&,如果结果位图位为1证明是文件交集。所用内存为1G

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

标记一个整数出现次数的状态:(用两个位标记一个整数)
出现0次:00
出现1次:01
出现2次及其以上:10

使用两个位图,bits1与bits2
出现一次bits1设置1,出现2次bits2设置1

void Set(size_t N)
{
	NUC::BitSet<-1>bits1;
	NUC::BitSet<-1>bits2;
	//00 ->01
	if (!bits1.Find(N) && !bits2.Find(N))//出现一次
	{
		bits2.Set(N);
	}
	else if (!bits1.Find(N) && bits2.Find(N))//01->10,出现2次
	{
		bits1.Set(N);
		bits2.ReSet(N);
	}
	else if(bits1.Find(N) && !bits2.Find(N))//10 ->10,出现2次以上
	{
		bits1.Set(N);
		bits2.ReSet(N);
	}
	else//发生错误
	{
		assert(false);
	}
}

代码位置

Github
Gitee

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NUC_Dodamce

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值