面试官:给你40亿个数,你应该如何快速判断一个数是否在这40亿个数中?—— 位图 [ C++入门 ]

news2024/11/29 22:39:28

在这里插入图片描述

阅读导航

  • 引言
  • 一、位图的概念
    • 1. 官方文档
    • 2. 基本概念
  • 二、位图的实现
    • 1. 插入
    • 2. 删除
    • 3. 查找
    • C++模拟实现
    • 🔴完整代码
  • 总结

引言

在C++编程中,位图是一种常用的数据结构,用于高效地表示大量的布尔值。它通过使用一个二进制位来表示每个元素的存在与否,从而节省了大量的内存空间。现在,让我们考虑这样一个问题:给定一个包含40亿个数的数据集合,我们需要快速判断一个特定的数是否存在于其中。如何有效地解决这个问题呢?

传统的线性搜索算法需要遍历整个数据集合,时间复杂度较高,无法满足实时性和高效性的要求。而位图算法则提供了一种更加优雅和高效的解决方案。通过将每个数映射到位图中的相应位置上,我们可以利用位运算来快速判断一个数是否存在

通过深入理解位图算法的原理和实现,我们将能够在处理大规模数据集合时提供高效的查找功能。无论是在数据处理、搜索引擎还是其他需要频繁进行数值查找的场景中,位图算法都将发挥重要的作用。让我们开始探索吧!坐稳扶好咱们要开车了😍

一、位图的概念

1. 官方文档

| ===============================
| 🔴 位图的官方文档介绍
| ===============================
在这里插入图片描述

2. 基本概念

位图是一种数据结构,用于高效地表示大量的布尔值。它通过使用一个二进制位来表示每个元素的存在与否,从而节省了大量的内存空间

具体来说,位图由一组二进制位(0或1)组成,其中每个位代表一个元素的存在状态。如果该位为1,则表示对应的元素存在;如果该位为0,则表示对应的元素不存在。通过这种方式,位图可以将大量的元素用较少的内存空间表示出来,从而提高了数据的处理效率。

在C++中,位图通常使用一个字符数组来表示。假设我们需要表示 n n n 个元素的存在状态,那么我们可以使用一个长度为 ( n / 8 + 1 ) (n/8+1) (n/8+1) 的整数数组来表示位图。其中,每个整数表示8个元素的存在状态。例如,如果第 i i i 个元素存在,则我们可以将第 i i i 个二进制位设置为1,并将其所在的整数存储在位图数组的第 i / 8 i/8 i/8 个位置上。比如:

在这里插入图片描述
通过这种方式,我们可以快速地进行元素的插入、删除和查找操作。在插入一个元素时,我们只需要将相应的二进制位设置为1即可;在删除一个元素时,我们只需要将相应的二进制位设置为0即可;在查找一个元素时,我们只需要检查相应的二进制位是否为1即可。后面我会详细介绍。

总之,位图是一种简单而高效的数据结构,可以用于处理大规模的布尔值集合。在C++编程中,我们可以使用位运算和整数数组来实现位图,并通过位图算法来提高数据的处理效率。

二、位图的实现

1. 插入

位图的插入操作是将一个元素的存在状态设置为1,使其在位图中被标记为存在

  1. 计算索引:首先,需要确定要插入的元素在位图中对应的索引位置。通常情况下,位图使用字符数组来表示,每个整数可以表示8个元素的存在状态。因此,我们可以通过将元素的值除以8来计算它在整数数组中的索引。

    int idx = n / 8;
    
  2. 计算偏移量:接下来,需要确定要插入的元素在位图中对应的二进制位的偏移量。偏移量是指元素在整数中的二进制位的位置。我们可以通过将元素的值模8来计算偏移量。

    int pos = n % 8;
    
  3. 设置二进制位:最后,通过使用位运算和按位或操作,将对应的二进制位设置为1,即将元素插入位图。

    bitmap[idx] |= (1 << pos);
    

    这里,(1 << pos) 表示将1左移pos位,得到一个只有第pos位为1的二进制数。然后,使用按位或操作符|=将这个二进制数与位图中对应索引的整数进行按位或运算,将对应的二进制位设置为1。

2. 删除

位图的删除操作是将一个元素的存在状态设置为0,使其在位图中被标记为不存在

  1. 计算索引:首先,需要确定要操作的元素在位图中对应的索引位置。由于每个char变量可以表示8个元素的存在状态,因此我们可以通过将元素的值除以8来计算它在char数组中的索引。

    int idx = n / 8;
    
  2. 计算偏移量:接下来,需要确定要操作的元素在位图中对应的二进制位的偏移量。由于每个char变量可以表示8个元素的存在状态,我们可以通过将元素的值模8来计算偏移量。

    int pos = n % 8;
    
  3. 清除二进制位:最后,通过使用位运算和按位与操作,将对应的二进制位设置为0,即将元素从位图中删除。

    bitmap[idx] &= ~(1 << pos);
    

这里,~(1 << pos) 表示将1左移pos位,得到一个只有第pos位为0的二进制数,然后使用按位与操作符&=将这个二进制数与位图中对应索引的整数进行按位与运算,将对应的二进制位设置为0。

3. 查找

在位图中进行查找操作时,我们可以通过检查对应的二进制位来确定元素是否存在

  1. 计算索引:首先,需要确定要查找的元素在位图中对应的索引位置。由于每个char变量可以表示8个元素的存在状态,因此我们可以通过将元素的值除以8来计算它在char数组中的索引。

    int idx = n / 8;
    
  2. 计算偏移量:接下来,需要确定要查找的元素在位图中对应的二进制位的偏移量。由于每个char变量可以表示8个元素的存在状态,我们可以通过将元素的值模8来计算偏移量。

    int pos = n % 8;
    
  3. 检查二进制位:使用按位与操作符&将对应的二进制位提取出来,并检查其值是否为1。

    int bit = bitmap[idx] & (1 << pos);
    

    这里,(1 << pos) 表示将1左移pos位,得到一个只有第pos位为1的二进制数。然后,使用按位与操作符&将这个二进制数与位图中对应索引的char变量进行按位与运算,提取出对应的二进制位。

  4. 判断结果:根据上一步得到的结果,判断元素是否存在于位图中。

    • 如果结果为0,则表示元素不存在于位图中。
    • 如果结果不为0,则表示元素存在于位图中。

C++模拟实现

  1. 类定义:定义了一个名为bitset的类,并使用模板参数size_t N指定位图的大小。

    template<size_t N>
    class bitset
    {
    public:
    	...
    private:
    	vector<char> _bits;
    };
    
  2. 构造函数:在构造函数中,首先通过将位图大小除以8并加1来计算需要分配的char数组的大小,然后使用vector容器创建一个大小为该值的char数组,并将所有元素初始化为0。

    bitset()
    {
        _bits.resize(N/8 + 1, 0);
    }
    
  3. set函数:set函数用于设置位图中的元素。它首先计算元素在位图中对应的索引和偏移量,然后使用按位或操作符|将对应的二进制位设置为1。

    void set(size_t x)
    {
        size_t i = x / 8;
        size_t j = x % 8;
    
        _bits[i] |= (1 << j);
    }
    
  4. reset函数:reset函数用于重置位图中的元素。它首先计算元素在位图中对应的索引和偏移量,然后使用按位与非操作符~和按位与操作符&将对应的二进制位设置为0。

    void reset(size_t x)
    {
        size_t i = x / 8;
        size_t j = x % 8;
    
        _bits[i] &= ~(1 << j);
    }
    
  5. test函数:test函数用于测试位图中的元素是否存在。它首先计算元素在位图中对应的索引和偏移量,然后使用按位与操作符&将对应的二进制位提取出来,并返回其值。

    bool test(size_t x)
    {
        size_t i = x / 8;
        size_t j = x % 8;
    
        return _bits[i] & (1 << j);
    }
    
  6. 私有成员变量:在类定义中,还定义了一个名为_bits的私有成员变量,用于存储位图中的数据。该变量是一个char类型的vector容器,大小为位图大小除以8并加1,所有元素初始化为0。

    private:
    	vector<char> _bits;
    

🔴完整代码

#include <vector>

template<size_t N>
class bitset
{
public:
    // 构造函数
    bitset()
    {
        // 计算需要分配的char数组的大小,并使用vector容器创建一个大小为该值的char数组
        // 将所有元素初始化为0
        _bits.resize(N/8 + 1, 0);
    }

    // 设置位图中的元素
    void set(size_t x)
    {
        // 计算元素在位图中对应的索引和偏移量
        size_t i = x / 8;
        size_t j = x % 8;

        // 使用按位或操作符|将对应的二进制位设置为1
        _bits[i] |= (1 << j);
    }

    // 重置位图中的元素
    void reset(size_t x)
    {
        // 计算元素在位图中对应的索引和偏移量
        size_t i = x / 8;
        size_t j = x % 8;

        // 使用按位与非操作符~和按位与操作符&将对应的二进制位设置为0
        _bits[i] &= ~(1 << j);
    }

    // 测试位图中的元素是否存在
    bool test(size_t x)
    {
        // 计算元素在位图中对应的索引和偏移量
        size_t i = x / 8;
        size_t j = x % 8;

        // 使用按位与操作符&将对应的二进制位提取出来,并返回其值
        return _bits[i] & (1 << j);
    }

private:
    // 存储位图数据的私有成员变量
    std::vector<char> _bits;
};

总结

位图是一种用于表示和操作大量二进制位的数据结构。它通过使用一个固定长度的数组来存储位的状态,每个位可以表示某种信息的存在与否。位图常用于解决需要高效存储和操作大量布尔类型数据的问题。在C++中,可以使用类似上述的位图类来模拟实现位图功能,实现插入、删除和查找等操作。位图的优点是占用空间小、操作高效,但受限于位数范围。

感谢您对博主文章的关注与支持!另外,我计划在未来的更新中持续探讨与本文相关的内容,会为您带来更多关于C++以及编程技术问题的深入解析、应用案例和趣味玩法等。请继续关注博主的更新,不要错过任何精彩内容!

再次感谢您的支持和关注。期待与您建立更紧密的互动,共同探索C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!

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

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

相关文章

集成电路自动化测试的优势是什么?

集成电路自动化测试的优势主要包括&#xff1a; 提高效率&#xff1a;自动化测试可以显著提高测试效率&#xff0c;因为可以24小时不间断地进行测试&#xff0c;同时避免了人工测试中的误差和疏漏&#xff0c;节省了大量时间和人力成本。 提升精度&#xff1a;自动化测试的精…

MySQL|基础操作+8大查询方式汇总

MySQL操作 一、操作数据库 MySQL中可以创建多个数据库用于存储不同场景的表结构&#xff0c;学习MySQL之前&#xff0c;我们要先理清如下的关系&#xff1a; 数据库 --> 数据表 --> 字段 抛开数据库存储上限去考虑&#xff0c;每个数据库中可以包含无数个数据表&#xf…

Apriori介绍及代码批注

一、Apriori原理解析 1. 概述 关联规则分析是数据挖掘中最活跃的研究方法之一&#xff0c;目的是在一个数据集中找到各项之间的关联关系&#xff0c;而这种关系并没有在数据中直接体现出来。以超市的销售数据为例&#xff0c;当存在很多商品时&#xff0c;可能的商品组合数量…

MySQL的数据库操作、数据类型、表操作

目录 一、数据库操作 &#xff08;1&#xff09;、显示数据库 &#xff08;2&#xff09;、创建数据库 &#xff08;3&#xff09;、删除数据库 &#xff08;4&#xff09;、使用数据库 二、常用数据类型 &#xff08;1&#xff09;、数值类型 &#xff08;2&#xff0…

用Jmeter做微信小程序项目接口测试【案例】

公司新项目组开发一款微信小程序电商平台&#xff0c;为了更好保证产品质量&#xff0c;因此提出了需要进行接口测试。 从接口本身来讲&#xff0c;对其测试与其他项目应该是一样的。所以不难理解&#xff0c;我们要对小程序的接口测试需要准备的 材料有&#xff1a; 1、完备…

LCR 158. 库存管理 II 哈希 / 摩尔投票法

LCR 158. 库存管理 II - 力扣&#xff08;LeetCode&#xff09; 仓库管理员以数组 stock 形式记录商品库存表。stock[i] 表示商品 id&#xff0c;可能存在重复。请返回库存表中数量大于 stock.length / 2 的商品 id。 &#xff08;1&#xff09;方法一&#xff1a;先排序 题目…

​Profinet转EtherNET/IP从站连接欧姆龙plc与西门子200smart通讯的配置方法​

本案例是200smart plc与欧姆龙plc进行通讯的方法&#xff0c;远创智控YC-PNM-EIP网关可以读写全系列西门子 PLC 数据。一般不需要 PLC 里做特殊的设置。只需要把 PLC 的变量地址配置到网关中&#xff0c;网关就可以读取指定地址的数据或者写数据到指定的地址。 PLC 通过网线连接…

Android---Bitmap详解

每一个 Android App 中都会使用到 Bitmap&#xff0c;它也是程序中内存消耗的大户&#xff0c;当 Bitmap 使用内存超过可用空间&#xff0c;则会报 OOM。 Bitmap 占用内存分析 Bitmap 用来描述一张图片的长、宽、颜色等信息&#xff0c;可用使用 BitmapFactory 来将某一路径下…

浮点数在计算机中的二进制表示

文章目录 Part.I IntroductionPart.II 原理Part.III 代码验证Reference Part.I Introduction 首先要了解一下下面的知识&#xff1a; 1 位有两种状态&#xff1a;0 或 11 字节&#xff08;byte&#xff09; 8 位&#xff08;bit&#xff09; 2 7 128 2^7128 27128&#xff1…

rhcsa简单的查询命令

时间 date -s "20021125 12:00:00" date 052312002002 一.简单语法 hwclock --查看硬件时间 hwclock --show 查看硬件时间 hwclock -w 同步硬件时间和系统时间 hwclock -s 将硬件时钟同步到系统时间 hwclock -r 查看当前时间 cal 查看当前月份日历 cal 10 …

nodejs+vue+elementui+express基于体质分析的个性化健身方案生成系统与设计

基于体质分析的个性化健身方案生成系统与设计 客户端&#xff1a; 1、在健身系统中可以自己选择课程&#xff08;需先充值&#xff0c;金额大于课程价格才可购买&#xff0c;否则提示余额不足&#xff0c;请充值&#xff09;&#xff0c;完成课程后由该课程的发布教练评价评分…

Python文件——使用Python读取txt文件

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 本文专栏&#xff1a;Python专栏 专栏介绍&#xff1a;本专栏为免费专栏&#xff0c;并且会持续更新python基础知识&#xff0c;欢迎各位订阅关注. 目录 一、文件的编码 1. 什么是编码 2. 常见的编码 二、P…

【软考】10.2 贪心法/回溯法/数据挖掘/智能优化

《贪心法》 适用于局部最优解 典型应用&#xff1a;背包问题 最有可能得到全局最优解&#xff1a;最大单位重量价值 ——> 重量 / 价值 《回溯法》 系统地搜索一个问题的所有解或者任一解深度优先&#xff0c;从根节点出发适用于解决迷宫类的问题 0-1背包算法的时间复杂度…

Python接口自动化之接口依赖!

以下主要介绍如何提取token、将token作为类属性全局调用及充值接口如何携带token进行请求。 一、场景说明 在面试接口自动化时&#xff0c;经常会问&#xff0c;其他接口调用的前提条件是当前用户必须是登录状态&#xff0c;如何处理接口依赖&#xff1f; 在此之前我们介绍过…

预训练机器阅读理解模型:对齐生成式预训练与判别式下游场景

©PaperWeekly 原创 作者 | 徐蔚文&#xff0c;李昕&#xff0c;邴立东等 单位 | 阿里巴巴达摩院 论文链接&#xff1a; https://arxiv.org/pdf/2212.04755.pdf 收录会议&#xff1a; NeurIPS 2023 论文链接&#xff1a; https://aclanthology.org/2023.acl-short.131.p…

038-第三代软件开发-简易视频播放器-自定义Slider (二)

第三代软件开发-简易视频播放器-自定义Slider (二) 文章目录 第三代软件开发-简易视频播放器-自定义Slider (二)项目介绍简易视频播放器自定义Slider (二)横向纵向 关键字&#xff1a; Qt、 Qml、 关键字3、 关键字4、 关键字5 项目介绍 欢迎来到我们的 QML & C 项目&…

进程(2)——进程状态(僵尸,睡眠……)【linux】

进程&#xff08;2&#xff09;——进程状态&#xff08;僵尸&#xff0c;睡眠……&#xff09;【linux】 一.操作系统的进程状态&#xff1a;1.1 运行态1.2 阻塞态1.3 挂起态 二.linux进程状态2.1 R——运行状态2.2 S——浅度睡眠状态2.3 D——&#xff08;disk sleep&#xf…

【数智化人物展】阿里元境CTO郭旷野:元宇宙技术推动数字内容生产变革

郭旷野 本文由阿里元境CTO郭旷野投递并参与《2023中国企业数智化转型升级先锋人物》榜单/奖项评选。 数据智能产业创新服务媒体 ——聚焦数智 改变商业 数字经济发展的大背景下&#xff0c;数字技术持续演进&#xff0c;逐渐形成了数字化生活、数字化生产模式&#xff0c;与此…

从工厂到社会:探索如何应用设计模式工厂模式

文章目录 &#x1f31f; 将设计模式工厂模式运用到社会当中&#x1f34a; 工厂模式在社会中的应用&#x1f389; 工厂&#x1f389; 餐厅&#x1f389; 运输 &#x1f34a; 工厂模式的优势&#x1f389; 代码简洁&#x1f389; 扩展性强&#x1f389; 便于维护和管理 &#x1f…

pytorch 笔记:KLDivLoss

1 介绍 对于具有相同形状的张量 ypred​ 和 ytrue&#xff08;ypred​ 是输入&#xff0c;ytrue​ 是目标&#xff09;&#xff0c;定义逐点KL散度为&#xff1a; 为了在计算时避免下溢问题&#xff0c;此KLDivLoss期望输入在对数空间中。如果log_targetTrue&#xff0c;则目标…