C++ 实现位图

引出

面试题:给出 40 亿个不重复的无符号整数,没有排过序。给定一个无符号整数,如何快速判断这个数是否在这 40 亿个无符号整数中。[ 腾讯面试题 ]

  • 想法一:将 40 亿个数据存放到 set 里面,然后再查找指定的无符号整数。
    时间复杂度:o( l o g 2 N log_2N log2N)。
  • 将 40 亿个无符号整数排序之后二分查找。
    我们先不考虑效率问题,实现上面的两种方案,都需要将 40 亿个整数加载到内存中。那么 40 亿个整数全部加载到内存中,需要多大空间呢?
    40 0000 0000 × 4 = 160 0000 0000 字节 = ( 160 0000 0000 ÷ 1024 ÷ 1024 ÷ 1024 ) G B ≈ 15 G B 40\space0000\space0000 \space \times \space 4 = 160\space0000\space0000\space字节 = \space (160 \space 0000 \space 0000 \space \div \space 1024 \space \div \space 1024 \space \div \space 1024) \space GB\space \approx \space 15 \space GB 40 0000 0000 × 4=160 0000 0000 字节= (160 0000 0000 ÷ 1024 ÷ 1024 ÷ 1024) GB  15 GB

好的,我们需要的内存大概是 15 GB。
对于 set 来说,除了数据域,还要维护其他的指针信息。15 GB 的内存还不够。
普通电脑的内存也就 16 GB左右吧!但是操作系统这个软件也需要内存空间哇!所以说用 set 和 排序加二分查找都不是理想的办法!


我们再来看看问题:题目的要求是判断一个数在不在!那么一个数在或者不在,只有两种状态。因此我们可以用一个二进制位来表示一个数在不在。
因此我们可以开辟 2 32 = 42 9496 7296 2^{32} = 42 \space 9496 \space 7296 232=42 9496 7296 个比特位,用哈希表中的直接定址法(哈希表的直接定址法开辟的空间需要包含给出的 40 亿个整数的范围),将 40 亿个整数一一映射到不同的比特位。
那么我们需要的空间:
( 2 32 ÷ 8 ÷ 1024 ÷ 1024 ) M B = 512 M B (2^{32} \space \div 8 \space \div 1024 \space \div 1024) \space MB = 512 \space MB (232 ÷8 ÷1024 ÷1024) MB=512 MB
这样一来,内存的问题是不是就轻松搞定啦!
上面使用的方法就是位图

位图基本结构定义

  • 在 C++ 中我们并没有比特位的类型,因此在位图的底层,使用的数据结构就是 vecctor<char> 或者 vector<int>。只要数据类型支持位运算就没多大问题。
  • 我们应该如何确定位图中底层 vector 的空间大小呢?首先这个 vector 肯定不能是静态的。因此就需要我们传参数来决定 vector 的大小。当然你可以在 bitmap (位图) 的构造函数做文章。但是库函数中并不是这么实现的。
    你还记得模板的非类型模板参数吗?如果你需要复习,点击这里:​➡️ ​C++模板详解
    我们定义一个非类型模板的参数,这样就可以不在构造函数传参啦!
#pragma once
#include<cstddef>
#include<vector>
namespace Tchey
{template <size_t N>class bitmap{private:vector<char> _a;};
}

构造函数

我们接收了一个非类型的模板参数,根据这个非类型的模板参数,我们就能确定 vector 的大小啦!

  • 如果你实现的 vector 存储的数据类型是 int。那么构造函数中,你的 vector 需要的空间大小就是:
    ( 非类型模板参数  N ÷ 32 ) + 1 (非类型模板参数\space N \space \div \space 32 ) + 1 (非类型模板参数 N ÷ 32)+1
    这个加一是为了应对需要的空间不是 int 类型的整数倍的情况,这是必须的。
  • 如果你实现的 vector 存储的数据类型是 char。那么构造函数中,你的 vector 需要的空间大小就是:
    ( 非类型模板参数  N ÷ 8 ) + 1 (非类型模板参数\space N \space \div \space 8 ) + 1 (非类型模板参数 N ÷ 8)+1

这样看起来 vector 存储 char 更加节省内存呢?只不过这点节省好像真的没多大必要呢!

bitmap()
{int size = N / 8 + 1;_a.resize(size);
}

void set(size_t x)

这个函数将参数 x 映射的那个位置在位图中标记为 1。
那么,我们拿到参数 x 如何将这个位置标记为 1 呢?在下图中,红色矩形是一个位图,红色矩形中的黑色矩形是一个一个的 char,假设我们要将下图中 x = 12 的位置标记为 1。
在这里插入图片描述

  • 因为我们的 vector 存储的是 char ,我们要找到 x 在 vector 中的哪个下标,只需要将 x ÷ 8 x \div 8 x÷8 即可。不妨记作: i
  • 除了获取 x 在 vector 中的哪一个下标,我们还需要获取 x 在这个下标的哪一位!我们只需要将 x % 8 x \space \% \space 8 x % 8 即可。不妨记作:j
    那要如何修改呢?只要让将 i 下标的 char1 << j 相或即可。

在这里你可能会想到大小端的问题,只能说我们这样做完全没有问题呢!大小端无非是高地址存储 char 的高位还是低位的问题。我们定义了将 x 置为 1 的逻辑。只要查找和置零按照同样的逻辑来就行。只不过在内存上对应的位置并不会像上面的示意图那样按照顺序来罢了!

void reset(size_t x)

这个函数将参数 x 映射的那个位置在位图中标记为 0。
关于给定参数 x 怎么找到 x 在 vector 中的哪一个下标(不妨记作 i),在这个下标的哪一位(不妨记作 j),在 set 函数中就已经讲解完毕了。这里就不再赘述啦!
那怎么将 x 对应的那个位置的比特位置为 0 呢?其实很简单:(~(1 << j)) & _a[i]
如果真的不理解就可以举一个例子呢!
如下图我们想将橙色位置的比特位标记为 0。
在这里插入图片描述

bool test(size_t x)

这个函数可以检测 x 对应的比特位是否是 1,如果 x 对应的比特位为 1 返回 true;反之返回 false。
实现:同样的我们需要计算出 x 对应的比特位在 vector 的哪个下标(不妨记作 i ),在这个下标的哪一位(不妨记作 j)。我们只需要返回:_a[i] & (1 << j) 即可。如果 (1 << j) 与 _a[i] 向与的结果为 0,则说明 _a[i] 的第 j 为就是 0。否则就是 1。因此我们返回相与的结果就好了。

bool test(size_t x)
{size_t i = x / 8;size_t j = x % 8;return _a[i] & (1 << j);
}

测试代码

#include<iostream>
#include<vector>
#include<cstddef>
using namespace std;
#include "bitmap.h"int main()
{Tchey::bitmap<10> bm;bm.set(1);bm.set(3);bm.set(5);bm.set(7);bm.set(9);for(int i = 0; i < 10; i++){cout << bm.test(i) << " ";}cout << endl;bm.reset(1);bm.reset(3);bm.reset(5);bm.reset(7);bm.reset(9);for(int i = 0; i < 10; i++){cout << bm.test(i) << " ";}cout << endl;return 0;
}

在这里插入图片描述
我们看到输出结果是没有任何问题的哈!那么我们的 bitmap 就模拟实现完成啦!

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

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

相关文章

论文阅读——MCAN(cvpr2019)

补充一下MCAN-VQA&#xff1a; 对图片的处理&#xff1a;首先输入图片到Faster R-CNN&#xff0c;会先设定一个判断是否检测到物体的阈值&#xff0c;这样动态的生成m∈[10,100]个目标&#xff0c;然后从检测到的对应的区域通过平均池化提取特征。第i个物体特征表示为&#xff…

MUYUCMS v2.1:一款开源、轻量级的内容管理系统

MuYuCMS&#xff1a;一款基于Thinkphp开发的轻量级开源内容管理系统&#xff0c;为企业、个人站长提供快速建站解决方案。它具有以下的环境要求&#xff1a; 支持系统&#xff1a;Windows/Linux/Mac WEB服务器&#xff1a;Apache/Nginx/ISS PHP版本&#xff1a;php > 5.6 (…

发布鸿蒙的第一个java应用

1.下载和安装华为自己的app开发软件DevEco Studio HUAWEI DevEco Studio和SDK下载和升级 | HarmonyOS开发者 2.打开IDE新建工程&#xff08;当前用的IDEA 3.1.1 Release&#xff09; 选择第一个&#xff0c;其他的默认只能用(API9)版本&#xff0c;搞了半天才发现8&#xff…

11 月 25 日 ROS 学习笔记——3D 建模与仿真

文章目录 前言一、在 ROS 中自定义机器人的3D模型1. 在 rviz 里查看3D模型2. xacro 二、Gazebo1. urdf 集成 gazebo2. 综合应用1). 运动控制及里程计2). 雷达仿真3). 摄像头信息仿真4). kinect 深度相机仿真5). 点云 前言 本文为11 月 25 日 ROS 学习笔记——3D 建模与仿真&am…

STK Components 基础篇

1.开发包 STK Components 访问AGI官网&#xff0c;注册并登录后&#xff0c;从官网下载开发包&#xff1a;https://support.agi.com/downloads/&#xff0c;下载成功后可以申请许可证&#xff0c;AGI会向你注册的邮箱地址发送有效期半年的使用授权许可文件&#xff08;lic文件…

最详细手把手教你安装 Git + TortoiseGit 及使用

软件下载 从 Git 官网 下载 Git 安装程序&#xff0c;点击 Download for Windows&#xff1a; 点击下载 64-bit Git for Windows Setup: Git for Windows Setup 为安装版本&#xff0c;建议选择此版本Git for Windows Portable 为绿色免安装版本 从 TortoiseGit 官网 下载 T…

Spring Boot + hutool 创建海报图片

Spring Boot hutool 创建海报图片 /*** 分享,生成图片* param id* return*/GetMapping("/getShareImg")public void getShareImg(String id,HttpServletResponse response) throws IOException {CouponConsignSaleClassify byId couponConsignSaleClassifyService…

OpenCV简介及安装

前言 因为最近想做图像处理、人脸检测/识别之类的相关开发&#xff0c;所以就开始补OpenCV的相关知识&#xff0c;便开个专栏用于记录学习历程和在学习过程中遇到的一些值得注意的重点和坑。 学习过程基本上也是面向官方文档和Google。 简介 OpenCV(开源的计算机视觉库)是基于…

C编译过程

寻觅GCC 如果你已经安装了Clion&#xff0c;那么gcc就在根目录下。 如果没有&#xff0c;那么需要去minGW的官网下载安装。添加到环境变量中。 编写C代码 #include <stdio.h>#define ARRAY_SIZE(a) sizeof(a)/sizeof(a[0]) static int a 123;int main() {int i 0;c…

【云备份】文件操作实用工具类设计

文章目录 为什么要单独设计文件工具类&#xff1f;整体实现Filesize ——文件大小stat接口 LastMTime ——最后一次修改时间LastATime —— 最后一次访问时间FileName —— 文件名称GetPostLen ——获取文件指定位置 指定长度的数据GetContnet —— 读取文件数据SetContent ——…

云计算领域的第三代浪潮!

根据IDC不久前公布的数据&#xff0c;2023年上半年中国公有云服务整体市场规模(IaaS/PaaS/SaaS)为190.1亿美元&#xff0c;阿里云IaaS、PaaS市场份额分别为29.9%和27.9%&#xff0c;都远超第二名&#xff0c;是无可置疑的行业领头羊。 随着人工智能&#xff08;AI&#xff09;…

cmake install接口常用方式介绍

cmake install接口常用方式介绍 1 Synopsis2 Introduction2.1 DESTINATION <dir>2.2 PERMISSIONS <permission>...2.3 CONFIGURATIONS <config>...2.4 COMPONENT <component>2.5 EXCLUDE_FROM_ALL2.6 RENAME <name>2.7 OPTIONAL 3 Signatures4 E…

走近科学之《MySQL 的秘密》

走近科学之《MySQL 的秘密》 mysql 存储引擎、索引、执行计划、事务、锁、分库分表、优化 1、存储引擎&#xff08;storage engines&#xff09; 存储引擎规定了数据存储时的不同底层实现&#xff0c;如存储机制、索引、锁、事务等。 可以通过 show engines 命令查看当前服务…

在 STM32 上实现温度补偿和校正

本文介绍了如何在 STM32 微控制器上实现温度补偿和校正&#xff0c;以提高温度传感器的测量精度。首先&#xff0c;我们将简要介绍温度补偿和校正的原理和目的。然后&#xff0c;我们将详细讨论在 STM32 上实现温度补偿和校正的步骤和方法。同时&#xff0c;提供了一个简单的示…

Android逆向实战 - MIUI调起三方应用系统拦截弹窗分析

近期&#xff0c;发现在部分Android手机调起其他应用时&#xff0c;会弹出一个系统弹窗拦截调起&#xff0c;需要用户二次确认。经过内部众测&#xff0c;发现绝大多数是小米手机&#xff0c;而且跟Android版本没有直接关系&#xff0c;猜测是MIUI某次升级引入的功能。这篇文章…

oracle的debjob挂載及查詢

背景 有一個需求需要定時去執行一個produce&#xff0c;可以使用oracle的dbjob定時執行&#xff0c;相比較之前的vbs更加絲滑 --傳遞produce 開始的時間 頻率 declarea number;beginDBMS_JOB.SUBMIT(a,xx_warehouse_daliy_record_p;,to_date(202311230800,yyyymmddhh24mi),…

illuminate/database 使用 四

文档&#xff1a;Hyperf Database: Getting Started - Laravel 10.x - The PHP Framework For Web Artisans 因为hyperf使用illuminate/database&#xff0c;所以按照文章&#xff0c;看illuminate/database代码实现。 一、读写分离 根据文档读写的host可以分开。设置读写分…

管理类联考——写作——考点+记忆篇——论证有效性分析——记忆

文章目录 论证有效性分析得分要点、寻找漏洞方法论证有效性分析五大逻辑漏洞类型论证有效性分析的具体写法论证有效性分析十大解题思路&#xff08;上&#xff09;方法一&#xff1a;理想法方法二&#xff1a;极端法方法三&#xff1a;其他因素法方法四&#xff1a;可行性法 论…

Everything进行内网穿透搜索

文章目录 1\. 部署内网穿透1.1. 注册账号1.2. 登录1.3. 创建隧道 2\. 从外网访问Everything 借助cpolar可以让我们在公网上访问到本地的电脑 1. 部署内网穿透 1.1. 注册账号 在使用之前需要先进行注册cpolar cpolar secure introspectable tunnels to localhost 1.2. 登录 C…

grdle 的安装与配置 、gradle和jdk版本对应关系

java与gradle对应的版本关系 Java Java Gradle需要Java版本在8到19之间。目前还不支持Java 20及更高版本。 Java 6和Java 7仍然可以用于编译&#xff0c;但已经不适合用于测试。Gradle 9.0不支持Java 6和Java 7的测试。任何完全支持的Java版本都可以用于编译或测试。 然而&…