【数据结构】哈希经典应用:位图——[深度解析](8)

简介: 【数据结构】哈希经典应用:位图——[深度解析](8)

一.位图的基本概念

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

二.位图的原理

  • 哈希—— 直接定址法
  • 例:
  • 在实际场景中,我们的机器一般是 小端机(从左到右,从大到小排布)
  • 所以真正的场景一般如下:
  • 小端机性质 证明:

三.位图(bitset)的代码实现(逐过程解读)

【1】位图的文档查看

  • 我们可以重点关注红圈圈出的三个位图常用函数

【2】把X映射的那个标记成1——对应biteset中的set

【3】把X映射的那个标记成0——对应biteset中的reset

【4】判断某位是1还是0——对应biteset中的test

【5】位图的完整实现

#include<vector>
namespace bit
{
  template<size_t N>
  class bitset
  {
  public:
    bitset()
    {
      _a.resize(N / 32 + 1);//位图的初始化,确定分为多少块
    }
    // x映射的那个标记成1
    void set(size_t x)
    {
      size_t i = x / 32;
      size_t j = x % 32;
      _a[i] |= (1 << j);
    }
    // x映射的那个标记成0
    void reset(size_t x)
    {
      size_t i = x / 32;
      size_t j = x % 32;
      _a[i] &= (~(1 << j));
    }
    bool test(size_t x)
    {
      size_t i = x / 32;
      size_t j = x % 32;
      return _a[i] & (1 << j);
    }
  private:
    vector<int> _a;
  };
}

四.位图的经典应用场景

【※】对数据大小&转换的基本概念

  • 1G =1024 MB=10241024 BK=10241024*1024 Byte= 2^30 byte = 10亿+ byte
  • 例:我们判断40亿个整数需要多少G呢?
    分析:40亿个int,160亿byte,根据10亿byte对应1G,160亿byte对应16G

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

  • 分析:常规思路是遍历/排序+二分查找
  • 遍历的时间复杂度是O(N),排序(O(NlogN))+二分查找 logN
  • 显然对于40亿无符号整数来说,需要占用16G,占用资源过于庞大,不妥
  • 快速判断在不在,显然是位图经典场景,利用位图解决:
  • 数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。比如:

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

  • 分析:我们可以用两个位图来控制,我们可以这样设计
  • 代码展示设计思路如图所示:
template<size_t N>
  class twobitset
  {
  public:
    void set(size_t x)
    {
      // 00 -> 01
      if (!_bs1.test(x) && !_bs2.test(x))
      {
        _bs2.set(x);
      } // 01 -> 10
      else if (!_bs1.test(x) && _bs2.test(x))
      {
        _bs1.set(x);
        _bs2.reset(x);
      }
      // 本身10代表出现2次及以上,就不变了
    }
    bool is_once(size_t x)
    {
      return !_bs1.test(x) && _bs2.test(x);
    }
  private:
    bitset<N> _bs1;
    bitset<N> _bs2;
  };

【3】位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数

  • 此题的设计思路与上面的【2】基本一致,设计上要稍作改动:
  • 代码展示设计思路如图所示:
template<size_t N>
  class twobitset
  {
  public:
    void set(size_t x)
    {
      // 00 -> 01
      if (!_bs1.test(x) && !_bs2.test(x))
      {
        _bs2.set(x);                        //出现一次
      } // 01 -> 10
      else if (!_bs1.test(x) && _bs2.test(x))
      {
        _bs1.set(x);
        _bs2.reset(x);                    //出现两次
      }// 10 -> 11
      else if (_bs1.test(x) && !_bs2.test(x))
      {
        _bs2.set(x);                      //出现三次
      }
      // 此外代表出现3次及以上,就不变了
    }
    bool max_two(size_t x)
    {
      return (_bs1.test(x) && !_bs2.test(x))||(!_bs1.test(x) && _bs2.test(x));   //10 或者 01
    }
  private:
    bitset<N> _bs1;
    bitset<N> _bs2;
  };

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

  • 分析:
  • 第一种思路是:把其中一个文件存入位图,遍历另一个文件元素,将问题转变成"在不在"问题
  • 问题缺陷: 这种问题存在去重问题,即多次重复(下图中,交集明明只有一个3,但是会出现多个重复的3交集)
  • 分析:
  • 第二种思路是:将两个文件映射到两个位图中去(实现去重)
  • 如果相对应的位置都是1(满足相&为1),则此元素就在交集中


相关文章
|
8月前
|
机器学习/深度学习 文字识别 监控
安全监控系统:技术架构与应用解析
该系统采用模块化设计,集成了行为识别、视频监控、人脸识别、危险区域检测、异常事件检测、日志追溯及消息推送等功能,并可选配OCR识别模块。基于深度学习与开源技术栈(如TensorFlow、OpenCV),系统具备高精度、低延迟特点,支持实时分析儿童行为、监测危险区域、识别异常事件,并将结果推送给教师或家长。同时兼容主流硬件,支持本地化推理与分布式处理,确保可靠性与扩展性,为幼儿园安全管理提供全面解决方案。
443 3
|
9月前
|
人工智能 API 开发者
HarmonyOS Next~鸿蒙应用框架开发实战:Ability Kit与Accessibility Kit深度解析
本书深入解析HarmonyOS应用框架开发,聚焦Ability Kit与Accessibility Kit两大核心组件。Ability Kit通过FA/PA双引擎架构实现跨设备协同,支持分布式能力开发;Accessibility Kit提供无障碍服务构建方案,优化用户体验。内容涵盖设计理念、实践案例、调试优化及未来演进方向,助力开发者打造高效、包容的分布式应用,体现HarmonyOS生态价值。
629 27
|
9月前
|
存储 弹性计算 安全
阿里云服务器ECS通用型规格族解析:实例规格、性能基准与场景化应用指南
作为ECS产品矩阵中的核心序列,通用型规格族以均衡的计算、内存、网络和存储性能著称,覆盖从基础应用到高性能计算的广泛场景。通用型规格族属于独享型云服务器,实例采用固定CPU调度模式,实例的每个CPU绑定到一个物理CPU超线程,实例间无CPU资源争抢,实例计算性能稳定且有严格的SLA保证,在性能上会更加稳定,高负载情况下也不会出现资源争夺现象。本文将深度解析阿里云ECS通用型规格族的技术架构、实例规格特性、最新价格政策及典型应用场景,为云计算选型提供参考。
|
9月前
|
数据采集 机器学习/深度学习 存储
可穿戴设备如何重塑医疗健康:技术解析与应用实战
可穿戴设备如何重塑医疗健康:技术解析与应用实战
381 4
|
9月前
|
负载均衡 JavaScript 前端开发
分片上传技术全解析:原理、优势与应用(含简单实现源码)
分片上传通过将大文件分割成多个小的片段或块,然后并行或顺序地上传这些片段,从而提高上传效率和可靠性,特别适用于大文件的上传场景,尤其是在网络环境不佳时,分片上传能有效提高上传体验。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
1078 9
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
314 59
|
6月前
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
147 0
栈区的非法访问导致的死循环(x64)
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。
|
11月前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
565 77

推荐镜像

更多
  • DNS