【C++补充】第一弹---位图技术揭秘:内存优化与快速访问

news2025/1/12 5:43:36

 ✨个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】

目录

1 位图

1.1 位图相关面试题

1.2 位图的设计及实现

1.3 C++库中的位图 bitset

1.4 位图的模拟实现

1.5 位图的优缺点

1.6 位图相关考察题目


1 位图

1.1 位图相关面试题


1. 面试题
给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。(本题为腾讯/百度等公司出过的⼀个⾯试题)

解题思路1:暴力遍历,时间复杂度O(N),太慢

解题思路2:排序+⼆分查找。时间复杂度消耗 O(N*logN) + O(logN)

深入分析:解题思路2是否可⾏,我们先算算40亿个数据⼤概需要多少内存。
1G = 1024MB = 1024*1024KB = 1024*1024*1024Byte 约等于10亿多Byte
那么40亿个整数约等于16G,说明40亿个数是无法直接放到内存中的,只能放到硬盘文件中。而⼆分查找只能对内存数组中的有序数据进行查找。
解题思路3:数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用⼀个⼆进制比特位来代表数据是否存在的信息,如果⼆进制比特位为1,代表存在,为0代表不存在。那么我们设计⼀个用位表示数据是否存在的数据结构,这个数据结构就叫位图

比如:

1.2 位图的设计及实现


位图本质是⼀个直接定址法的哈希表,每个整型值映射⼀个bit位,位图提供控制这个bit的相关接⼝。

实现中需要注意的是,C/C++没有对应位的类型,只能看int/char这样整形类型,我们再通过位运算去控制对应的比特位。比如我们数据存到vector<int>中,相当于每个int值映射对应的32个值,比如第⼀个整形映射0-31对应的位,第⼆个整形映射32-63对应的位,后⾯的以此类推,那么来了⼀个整形值x,i = x / 32;j = x % 32;计算出x映射的值在vector的第i个整形数据的第j位

解决给40亿个不重复的⽆符号整数,查找⼀个数据的问题,我们要给位图开2^32个位,注意不能开40亿个位,因为映射是按大小 映射的,我们要按数据大小范围开空间,范围是是0-2^32-1,所以需要开2^32个位。然后从文件中依次读取每个set到位图中,然后就可以快速判断⼀个值是否在这40亿个数中了。

注意:C++库中的位图不能直接开辟2^32个位的空间,需要通过指针new这么大的空间,代码如下:

#include<iostream>
#include<bitset>
using namespace std;

int main()
{
	// 开2^32个位的位图
	//std::bitset<-1> bs1;// C++库开不了这么大空间
	//std::bitset<0xffffffff> bs2;
	// INT_MAX ->2,147,483,647 ->7FFF FFFF
	// UINT_MAX -> 0xffffffff
	//std::bitset<UINT_MAX> bs3;
	std::bitset<-1>* ptr = new std::bitset<-1>();// C++库能够new这么多空间
	return 0;
}

开辟成功 

开辟失败 

快速判断⼀个值是否在这40亿个数中 ?

代码演示

#include<iostream>
#include<bitset>
using namespace std;

int main()
{
	std::bitset<-1>* ptr =  new std::bitset<-1>();
	int a[] = { 5,7,9,2,5,9,5,5,7,5,3,9,2,5,1,5,6,6,6,6,7,9 };
	for (auto e : a)
	{
		ptr->set(e);
	}
	// 判断一个数是否在上面的数组中
	for (size_t i = 0; i < 10; i++)
	{
		if (ptr->test(i))
		{
			cout << i << "->" << "在" << endl;
		}
		else
		{
			cout << i << "->" << "不在" << endl;
		}
	}
	return 0;
}

运行结果 

1.3 C++库中的位图 bitset

可以看到核心接⼝还是set/reset/和test,当然后面还实现了⼀些其他接⼝,如to_string将位图按位转成01字符串,再包括operator[]等⽀持像数组⼀样控制⼀个位的实现

代码演示

#include<iostream>
#include<bitset>
using namespace std;

int main()
{
	bitset<100> bs1;
	// 返回位数,非类型参数N
	cout << bs1.size() << endl;
	// 位数为1个总个数
	cout << bs1.count() << endl;
	bitset<10> bs2(5);
	cout << bs2.count() << endl;
	cout << bs2 << endl;
	// 将第一位设置为1
	bs2.set(1);
	cout << bs2 << endl;
	// 将第零为设置为0
	bs2.reset(0);
	cout << bs2 << endl;
	// 检查第二位是否为1
	cout << bs2.test(2) << endl;
	return 0;
}

运行结果

1.4 位图的模拟实现

 基本结构

template<size_t N>
class bitset
{
public:
    // 构造函数
	bitset();
	// x映射的位标记成1
	void set(size_t x);
	// x映射的位标记成0
	void reset(size_t x);
	// x映射的位是1返回真,0返回假
	bool test(size_t x);
private:
	vector<int> _bs;
};

构造函数 

bitset()
{
	// 扩容,不管能否整除都多开一个空间
	_bs.resize(N / 32 + 1);
}

set() 

// x映射的位标记成1
void set(size_t x)
{
	size_t i = x / 32;
	size_t j = x % 32;

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

分析 

reset() 

// x映射的位标记成0
void reset(size_t x)
{
	size_t i = x / 32;
	size_t j = x % 32;

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

分析

 

test()

// x映射的位是1返回真,0返回假
bool test(size_t x)
{
	size_t i = x / 32;
	size_t j = x % 32;

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

分析 

1.5 位图的优缺点

优点:增删查改快,节省空间
缺点:只适用于整形


1.6 位图相关考察题目


• 给定100亿个整数,设计算法找到只出现⼀次的整数?
虽然是100亿个数,但是还是按范围开空间,所以还是开2^32个位,跟前面的题目是⼀样的。
• 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
把数据读出来,分别放到两个位图,依次遍历,同时在两个位图的值就是交集
• ⼀个文件有100亿个整数,1G内存,设计算法找到出现次数不超过2次的所有整数 ?

根据上面的题意,我们需要通过位图实现一个能表示出现0次,1次,2次,多次的位图,因此可以实现一个两个位图成员的类。

基本类

namespace lin
{
	template<size_t N>
	class twobitset
	{
	public:
		void set(size_t x)
		{
			bool bit1 = _bs1.test(x);
			bool bit2 = _bs2.test(x);

			if (!bit1 && !bit2) // 00->01
			{
				_bs2.set(x);
			}
			else if (!bit1 && bit2) // 01->10
			{
				_bs1.set(x);
				_bs2.reset(x);
			}
			else if (bit1 && !bit2) // 10->11
			{
				_bs1.set(x);
				_bs2.set(x);
			}
		}

		// 返回0 出现0次数
		// 返回1 出现1次数
		// 返回2 出现2次数
		// 返回3 出现2次及以上
		int get_count(size_t x)
		{
			bool bit1 = _bs1.test(x);
			bool bit2 = _bs2.test(x);

			if (!bit1 && !bit2)
			{
				return 0;
			}
			else if (!bit1 && bit2)
			{
				return 1;
			}
			else if (bit1 && !bit2)
			{
				return 2;
			}
			else
			{
				return 3;
			}
		}

	private:
		bitset<N> _bs1;
		bitset<N> _bs2;
	};
}

1、 只出现⼀次的整数?

代码演示

void test_twobitset1()
{
	lin::twobitset<100> tbs;
	int a[] = { 5,7,9,2,5,99,5,5,7,5,3,9,2,55,1,5,6,6,6,6,7,9 };
	for (auto e : a)
	{
		tbs.set(e);
	}
	// 只出现一次的整数
	for (size_t i = 0; i < 100; ++i)
	{
		if (tbs.get_count(i) == 1)
		{
			cout << i << endl;
		}
	}
}

运行结果 

2、找到两个文件交集? 

代码演示

void test_bitset()
{
	int a1[] = { 5,7,9,2,5,99,5,5,7,5,3,9,2,55,1,5,6 };
	int a2[] = { 5,3,5,99,6,99,33,66 };

	bitset<100> bs1;
	bitset<100> bs2;

	for (auto e : a1)
	{
		bs1.set(e);
	}

	for (auto e : a2)
	{
		bs2.set(e);
	}
	// 两个文件的交集
	for (size_t i = 0; i < 100; i++)
	{
		// 第i的位置都是1就是交集
		if (bs1.test(i) && bs2.test(i))
		{
			cout << i << endl;
		}
	}
}

运行结果 

 3、出现次数不超过2次的所有整数(出现1次和2次)?

代码演示

void test_twobitset2()
{
	lin::twobitset<100> tbs;
	int a[] = { 5,7,9,2,5,99,5,5,7,5,3,9,2,55,1,5,6,6,6,6,7,9 };
	for (auto e : a)
	{
		tbs.set(e);
	}
	// 出现次数不超过2次的所有整数
	for (size_t i = 0; i < 100; ++i)
	{
		//cout << i << "->" << tbs.get_count(i) << endl;
		if (tbs.get_count(i) == 1 || tbs.get_count(i) == 2)
		{
			cout << i << endl;
		}
	}
}

运行结果 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2275333.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决nginx多层代理后应用部署后访问发现css、js、图片等样式加载失败

一般是采用前后端分离部署方式&#xff0c;被上一层ng代理后&#xff0c;通过域名访问报错&#xff0c;例如&#xff1a;sqx.com.cn/应用代理路径。 修改nginx配置&#xff0c;配置前端页面的路径&#xff1a; location / {proxy_pass http://前端页面所在服务器的IP:PORT;pro…

第34天:安全开发-JavaEE应用反射机制攻击链类对象成员变量方法构造方法

时间轴&#xff1a; Java反射相关类图解&#xff1a; 反射&#xff1a; 1、什么是 Java 反射 参考&#xff1a; https://xz.aliyun.com/t/9117 Java 提供了一套反射 API &#xff0c;该 API 由 Class 类与 java.lang.reflect 类库组成。 该类库包含了 Field 、 Me…

Qt天气预报系统获取天气数据

Qt天气预报系统获取天气数据 1、获取天气数据1.1添加天气类头文件1.2定义今天和未来几天天气数据类1.3定义一个解析JSON数据的函数1.4在mainwindow中添加weatherData.h1.5创建今天天气数据和未来几天天气数据对象1.6添加parseJson定义1.7把解析JSON数据添加进去1.8添加错误1.9解…

SQL SERVER 2016 创建用户。

一、在实例中创建用户 二、在数据库中创建用户分配表格权限. 三、也可以在表格属性中分配用户权限 四、搜索对象中可以选择表、视图等等内容.

汽车信息安全 -- S32K1如何更新BOOT_MAC

目录 1.安全启动模式回顾 2.为什么要讨论BOOT_MAC 3.S32K1如何更新? 1.安全启动模式回顾 之前提到过,S32K1系列提供了Crypto Service Engine硬件加密模块(简称CSEc),大家可以通过该芯片系统寄存器SDID.FEATURES(System Device Identification Register)来判断自己的片子…

7 分布式定时任务调度框架

先简单介绍下分布式定时任务调度框架的使用场景和功能和架构&#xff0c;然后再介绍世面上常见的产品 我们在大型的复杂的系统下&#xff0c;会有大量的跑批&#xff0c;定时任务的功能&#xff0c;如果在独立的子项目中单独去处理这些任务&#xff0c;随着业务的复杂度的提高…

智慧城市应急指挥中心系统平台建设方案

建设背景与目标 智慧城市应急指挥中心系统平台的建设&#xff0c;源于对城市管理精细化、智能化的迫切需求。平台旨在通过整合各方资源&#xff0c;实现应急事件的快速响应与高效处置&#xff0c;提升城市安全管理水平。 前端设计与信息采集 前端设计注重立体化、全方位信息…

Playwright实战:Locators(定位器)指南

Locators Locators是Playwright自动等待和重试能力的核心部分。简而言之&#xff0c;Locators代表了一种随时在页面上查找元素的方法。 快速指南 这些是推荐的内置定位器。 page.getbyrole()通过显式和隐式可访问性属性进行定位。page.get_by_text()用于按文本内容定位。pa…

HTTP 核心概念

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

centos7.6 安装nginx 1.21.3与配置ssl

1 安装依赖 yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel2 下载Nginx wget http://nginx.org/download/nginx-1.21.3.tar.gz3 安装目录 mkdir -p /data/apps/nginx4 安装 4.1 创建用户 创建用户nginx使用的nginx用户。 #添加www组 # groupa…

Homestyler 和 Tripo AI 如何利用人工智能驱动的 3D 建模改变定制室内设计

让设计梦想照进现实 在Homestyler,我们致力于为每一个梦想设计师提供灵感的源泉,而非挫折。无论是初学者打造第一套公寓,或是专业设计师展示作品集,我们的直观工具都能让您轻松以惊人的3D形式呈现空间。 挑战:实现定制设计的新纪元 我们知道,将个人物品如传家宝椅子、…

深度学习知识点:LSTM

文章目录 1.应用现状2.发展历史3.基本结构4.LSTM和RNN的差异 1.应用现状 长短期记忆神经网络&#xff08;LSTM&#xff09;是一种特殊的循环神经网络(RNN)。原始的RNN在训练中&#xff0c;随着训练时间的加长以及网络层数的增多&#xff0c;很容易出现梯度爆炸或者梯度消失的问…

ASP.NET Core 中服务生命周期详解:Scoped、Transient 和 Singleton 的业务场景分析

前言 在 ASP.NET Core 中&#xff0c;服务的生命周期直接影响应用的性能和行为。通过依赖注入容器 (Dependency Injection, DI)&#xff0c;我们可以为服务定义其生命周期&#xff1a;Scoped、Transient 和 Singleton。本文将详细阐述这些生命周期的区别及其在实际业务中的应用…

kubeneters-循序渐进Cilium网络(二)

文章目录 概要IP 地址配置接口配置解析结论 概要 接续前一章节&#xff0c;我们还是以这张图继续深入Cilium网络世界 IP 地址配置 通过检查 Kubernetes 集群的当前环境&#xff0c;可以获取实际的 IP 地址和配置信息。这些信息将被补充到之前的网络示意图中&#xff0c;以使…

树莓派设备树编译

上回书讲到&#xff1a; 树莓派 OS 安装 树莓派内核 kernel 编译 今天我们介绍下树莓派设备树如何添加、编译和使用。 设备树初识 设备树&#xff08;Device Tree, DT&#xff09;是 Linux 内核用来描述硬件的一种数据结构。它以一种结构化的方式定义了硬件的配置和属性&a…

微信小程序map组件所有markers展示在视野范围内

注意&#xff1a;使用include-points属性不生效&#xff0c;要通过createMapContext实现 <template><view class"map-box"><map id"map" class"map" :markers"markers" :enable-traffic"true" :enable-poi&…

省市区三级联动(后端)

前提&#xff1a;springboot、mybatis-plus、swagger 数据库&#xff1a; 文章顶部 实体类&#xff1a; City package com.itfly.entity;import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import java.time.Loca…

【Axure高保真原型】环形进度条(开始暂停效果)

今天和大家分享环形进度条&#xff08;开始暂停效果&#xff09;的原型模版&#xff0c;效果包括&#xff1a; 点击开始按钮&#xff0c;可以环形进度条开始读取&#xff0c;中部百分比显示环形的读取进度&#xff1b; 在读取过程中&#xff0c;点击暂停按钮&#xff0c;可以随…

CTFshow—文件包含

Web78-81 Web78 这题是最基础的文件包含&#xff0c;直接?fileflag.php是不行的&#xff0c;不知道为啥&#xff0c;直接用下面我们之前在命令执行讲过的payload即可。 ?filephp://filter/readconvert.base64-encode/resourceflag.php Web79 这题是过滤了php&#xff0c;…

Jenkins pipeline 发送邮件及包含附件

Jenkins pipeline 发送邮件及包含附件 设置邮箱开启SMTP服务 此处适用163 邮箱 开启POP3/SMTP服务通过短信获取TOKEN &#xff08;保存TOKEN, 后面Jenkins会用到&#xff09; Jenkins 邮箱设置 安装 Build Timestamp插件 设置全局凭证 Dashboard -> Manage Jenkins …