zoukankan      html  css  js  c++  java
  • 位图

    位图(bitset)是一种常用的数据结构,常用在给一个很大范围的数,判断其中的一个数是不是在其中。这篇文章主要给大家介绍了关于C++位图以及位图的实现原理与方法,需要的朋友可以参考下

    位图就是bitmap的缩写,所谓bitmap,就是用每一位来存放某种状态,适用于大规模数据,该数据都是不重复的简单数据。通常是用来判断某个数据存不存在的

    例如:给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中

    如果不看数据量,我们第一想到的肯定就是依次从头遍历,但是这个数据量是非常大的,有40亿,遍历40亿次消耗的时间和内存是非常多的。但是引入位图后,就可以专门解决这种大量数据查找是否存在的问题。查找这个数是否存在所消耗的时间复杂度为O(1),且节省了32倍的容量(下面有解释)。下面我们一起来看看位图的原理及代码实现

    查找一个数是否存在,其实答案就是存在或者不存在,这种只需要回答是与否的问题,我们都可以用二进制中的位来表示,1表示该数存在,反之0表示该数不存在。而位图中的每个数据单元都是一个bit位,这样子平时我们都要话32位4字节来存储数据,而现在我们只需要花1个字节就能“存储数据”,在空间上减少了约32倍的容量。例如40G的数据我们只要花1.3G来存储。但是我们平时操作的数据类型最小就是一个字节,我们不能直接对位进行操作,所以我们可以借助位运算来对数据进行操作。下面我们来看看数据在位图中是如何存储的

    我们这里给出一个数组

    int arr[] = {1,2,4,5,7,10,11,14,16,17,21,23,24,28,29,31};则我们只需要花1个字节来存这些数据

     解释:我们目前很多的机器都是小端存储,也就是低地址存低位,一个整形数据中,第一个字节用来存储0-7的数字,第二个字节用来存储8-15的数字,第三个字节用来存储16-23的数字,第四个字节用来存储24-31的数字。我们来看看数字10是如何存储的。先通过模上32,取余还是10,然后再将4字节中第10个比特位置为1,则表示该数字出现过。由于我们的机器是小端存储,所以我们的每个比特位都是要从右边开始计算的,如下图

    所以说我们只需要将对应的比特位置为1即可。但是如果我们要存储的数据很大呢?其实也很简单,我们可以定义一个数组,当做一个位图,如果该数字在0-31之间,我们就存储在0号下标的元素中进行操作,如果在32-63之间,则就在1号下标之间进行操作。计算下标我们可以通过模32来获得下标。

    我们知道位图的原理后,我们在通过原理来用代码实现一个位图吧

    成员变量和构造函数:在实现位图中,我们的成员变量只需要一个数组就可以实现。而这个数组有多我们要开多大呢?数组多开一个整形空间,就能多存32个数字,所以我们可以让用户提供一个准确的数,这个数是一个数据量,也是数的最大范围。我们可以通过该数模上32,就可以获得该数组的大小,但是0~31模上32为0,我们开0个空间那显然不合适,所以我们要开range/32 + 1个空间大小的数组

    存储数据:存储一个数字num需要3个步骤,第一是需要计算出该值对应的数组下标。计算数组下标方式为idx=num / 32;第二步是计算num在对应整数的比特位的位置bitIdx=num%32;第三步是要将计算出来的bite位置为1。我们之前说过,要操作位,我们可以通过位运算来操作,可以先将1左移bitIdx位后再和整数进行或运算

    例如假设bitIdx=5,数据为10010011

    1.将1进行左移5位==>100000

    2.将数据和第一步计算出来的结果进行或运算

    10010011 | 100000 =10110011,此时我们就将指定位置置位1了

    查找数据:要判断一个数据是否存在,其实和存储数据是类似,也是需要计算出两个位置idx和bitIdx。然后通过这两个位置来判断对应位置是否为1,为1则表示该数字存在。如何判断呢?我们可以先将数组下标为idx的整数向右移bitIdx位,然后再和1进行与运算,如果为1则表示存在,否则不存在

    例如假设bitIdx=5,数据为10110011

    1.将数据进行右移5位00000101

    2.将第一步计算出来的结果和1进行与运算

    00000101 & 1 = 1,此时表示该数字存在,返回true

    删除数据:删除数据和存储数据操作一样,唯一的区别就是将对应的bit位置为0。我们可以通过先将1进行左移bitIdx位,然后取反,将结果再和原来数据进行与运算

    例如假设bitIdx=5,数据为10110011

    1.将1进行左移5位后并取反011111

    2.将第一步计算出来的结果和数据进行与运算

    10110011 & 011111 = 10010011,删除成功

    class BitMap
    {
    public:
        //位图的内存大小和数据范围有关
        BitMap(size_t range)
            :_bit(range / 32 + 1)
        {}
     
        void set(const size_t num)
        {
            //计算数组中的下标
            int idx = num / 32;
            //计算num在对应下标整数中的下标位置
            int bitIdx = num % 32;
            //将对应的比特位置1
            _bit[idx] |= 1 << bitIdx;
        }
     
        bool find(const size_t num)
        {
            int idx = num / 32;
            int bitIdx = num % 32;
            return (_bit[idx] >> bitIdx) & 1;
        }
     
        void reset(const size_t num)
        {
            int idx = num / 32;
            int bitIdx = num % 32;
            _bit[idx] &= ~(1 << bitIdx);
        }
    private:
        vector<int> _bit;
    };

    C++位图的实现原理与方法

  • 相关阅读:
    SecureCRT
    MySQL
    流式计算概述
    WEB加密算法
    drop、truncate和delete的区别
    JVM参数
    HTTP1.0与HTTP1.1区别
    HTTP请求方法
    Java查看JVM转态的命令总结
    go二次更新
  • 原文地址:https://www.cnblogs.com/guxuanqing/p/15528114.html
Copyright © 2011-2022 走看看