【面试题】位图

简介: 【面试题】位图

位图

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

中。【腾讯】

  1. 遍历,时间复杂度O(N)
  2. 排序(O(NlogN)),利用二分查找: logN

申请512M的内存

一个bit位代表一个unsigned int值

读入40亿个数,设置相应的bit位

读入要查询的数,查看相应bit位是否为1,为1表示存在,为0表示不存在

我们可以用每一个二进制位来记录一个数据,一个int本来只能记录一个数据的,现在可以记录64个数字了,大大减少的空间。

在小端存储的机器上是这样存储的:数据高字节保存在内存的高地址中,数据的低字节保存在低地址中。

比如我要把19记录进去

19/8=2

19%8=3

从0开始算,原本19/8=2,因此是在第三个字节当中,第3个位置(位置从0开始算)。

如何添加数据

如果我要把6这个数据添加到位图当中,初始化的位图的状态是全0。

如下图所示(位图的初始状态):

0 0 0 0 0 0 0 0

6/8=0;

6%8=6;

因此就是在第0个字节中第6个位置。

可以构造出一个如下的二进制序列

0 1 0 0 0 0 0 0

将两个二进制序列**按位或(|)**一下,0|1=1,0|0=0,不会对其他位置的数据造成改变。

那么如何进行构造呢?

只要将1左移(<<)j个位置即可1<<j

如何删除数据

如果我要把6这个数据从位图当中删除

6/8=0;

6%8=6;

因此就是在第0个字节中第6个位置。

如下图所示(位图的初始状态):

0 1 0 0 0 0 0 0

可以构造出一个如下的二进制序列

1 0 1 1 1 1 1 1

将两个二进制序列**按位与(&)**一下,0&1=0,0&0=0,1&1=1,不会对其他位置的数据造成改变。

那么如何进行构造呢?

只要将1左移(<<)j个位置然后按位取反即可~(1<<j)

代码实现

namespace mudan
{
  template<size_t N>
  class bitset
  {
  public:
    bitset()
    {
      _bits.resize(N / 8 + 1, 0);//计算出需要多少字节来存储
    }
    void set(size_t x)
    {
      size_t i = x / 8;
      size_t j = x % 8;
      _bits[i] |= (1 << j);
    }
    void reset(size_t x)
    {
      size_t i = x / 8;
      size_t j = x % 8;
      _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:
    vector<char> _bits;
  };
  void test_bit_set1()
  {
    bitset<100> bs1;
    bs1.set(8);
    bs1.set(9);
    bs1.set(20);
    cout << bs1.test(8) << endl;
    cout << bs1.test(9) << endl;
    cout << bs1.test(20) << endl;
    cout <<"=====================" << endl;
    bs1.reset(8);
    bs1.reset(9);
    bs1.reset(20);
    cout << bs1.test(8) << endl;
    cout << bs1.test(9) << endl;
    cout << bs1.test(20) << endl;
  }
}

测试结果可以看到正常运行了,可以查到找这个数字,删除之后就无法查找到了。

给100亿个整数,如何找到只出现一次的数字

可以用两个位图来记录情况

分类:

出现0次:00

出现1次:01

1次以上:10

代码实现

template<size_t N>
  class twobitset
  {
  public:
    void set(size_t x)
    {
      bool inset1 = _bs1.test(x);
      bool inset2 = _bs2.test(x);
      //00的情况
      if (inset1 == false && inset2 == false)
      {
        //变成01,出现一次
        _bs2.set(x);
      }
      else if (inset1 == false && inset2 == true)
      {
        //出现了一次,变成一次以上
        _bs1.set(x);//1
        _bs2.reset(x);//0
      }
      else if (inset1 == true && inset2 == false)
      {
        //10->11
        _bs1.set(x);
        _bs2.set(x);
      }
    }
    void print_once_num()
    {
      for (size_t i = 0; i < N; ++i)
      {
        if (_bs1.test(i) == false && _bs2.test(i) == true)
        {
          cout << i << endl;
        }
      }
    }
  private:
    bitset<N> _bs1;
    bitset<N> _bs2;
  };
  void test_bit_set3()
  {
    int a[] = { 3, 4, 5, 2, 3, 4, 4, 4, 4, 12, 77, 65, 44, 4, 44, 99, 33, 33, 33, 6, 5, 34, 12 };
    twobitset<100> bs;
    for (auto e : a)
    {
      bs.set(e);
    }
    bs.print_once_num();
  }

可以看到,把只出现了一次的数字筛选出来了。

一次类推,出现两次三次四次五次六次都可以用这种方法。

给两个文件,分别有100亿个整数,但只有1g内存,如何找到文件的交集?

这个其实也简单,开两个位图,然后遍历位图是1 1就代表存在交集,反之没有。

1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数

和之前的解决方式类似,记录几种状态

0次:00

1次:01

2次:10

3次及以上:11

目录
相关文章
|
29天前
|
存储 Java
Bitmap位图(Java实现)
本文介绍了使用Java实现一个简单的Bitmap,通过自定义byte数组存储数据,提供put和exist方法分别用于插入数据和查询数据是否存在。Bitmap利用位操作高效地管理大量布尔值,适用于空间优化的场景。代码中详细解释了位图的核心原理、方法实现及边界检查。后续计划探讨位图在海量数据去重中的应用及JDK BitSet源码分析。
71 7
|
Java
如何用Java实现位图转矢量图?
通过前面几篇图片转字符、灰度图的文章介绍之后,接下来我们再来看一个有意思的东西,基于前文的基础,实现位图转矢量图的功能
1391 0
如何用Java实现位图转矢量图?
|
7月前
|
C++
位图和布隆过滤器:位图
位图和布隆过滤器:位图
|
8月前
|
存储 算法 C++
【C++入门到精通】位图 | 位图的实现[ C++入门 ]
【C++入门到精通】位图 | 位图的实现[ C++入门 ]
87 0
|
C++ 容器
【C++进阶】十一、哈希的应用---位图(一)
目录 一、位图的引入 二、位图的应用 三、位图的使用(bitset的使用) 3.1 介绍 3.2 使用 四、bitset(位图模拟实现)
159 0
【C++进阶】十一、哈希的应用---位图(一)
|
算法 C++
|
存储 数据采集 缓存
数据结构与算法必知--- Bitmap位图与布隆过滤器
数据结构与算法必知--- Bitmap位图与布隆过滤器
|
存储 缓存 Java
Bitmap知识点集合
今天聊聊Bitmap相关的面试题/知识点,看看你是否都弄明白了呢?
357 0
Bitmap知识点集合
|
算法 NoSQL C#
C#位图BitArray 小试牛刀
难缠的布隆过滤器,这次终于通透了
C#位图BitArray 小试牛刀