算法【位图】

特别提醒:Python同学实现位运算的题目需要特别注意,需要自己去手动处理溢出和符号扩展等问题。比如:

python 复制代码
(n << shift_amount) & 0xFFFFFFFF

一、位图原理

其实就是用bit组成的数组来存放值,用bit状态1、0代表存在、不存在,取值和存值操作都用位运算。限制是必须为连续范围且不能过大。好处是极大的节省空间,因为1个数字只占用1个bit的空间。

二、位图的实现

一个位图有以下几个函数:

cpp 复制代码
// 初始化位图 只支持0~n-1所有数字的增删改查
Bitset(int n);
// 把num加入到位图
void add(int num);
// 把num从位图中删除
void remove(int num);
// 如果位图里没有num 就加入  如果位图里有num 就删除
void reverse(int num);
// 查询num是否在位图中
bool contains(int num);

代码如下。

cpp 复制代码
class Bitset
{
public:
    int *arr;
    Bitset(int n);
    ~Bitset();
    void add(int num);
    void remove(int num);
    void reverse(int num);
    bool contains(int num);
};

Bitset::Bitset(int n)
{
    // 数组大小为n/32向上取整 可写成(n+31)/32
    arr = new int[(n+31)/32];
}

Bitset::~Bitset()
{
    delete [] arr;
}

void Bitset::add(int num){
    arr[num/32] |= (1 << (num % 32));
}

void Bitset::remove(int num){
    arr[num/32] &= ~(1 << (num % 32));
}

bool Bitset::contains(int num){
    return ~((arr[num/32] & (1 << (num % 32))) == 0);
}

void Bitset::reverse(int num){
    if(contains(num)){
        remove(num);
    }else{
        add(num);
    }
}

由于是用位代表一个数是否存在,所以一个int可代表32个数,则需要的int个数为n/32向上取整。对此可用(a + b -1)/b为a/b的向上取整来计算。对于一个数num,它被哪一个int代表由num/32得到下标,而具体的位则是由num%32得到。add方法中得到具体位数直接或进去,remove方法中得到具体的位数直接与进去。

下面有一个相关测试可以检验一下,按照题目要求修改部分即可。

测试链接:https://leetcode-cn.com/problems/design-bitset/

代码如下。

cpp 复制代码
class Bitset {
public:
    // 根据数据量提前申请
    int arr[3126] = {0};
    // 是否翻转 false为没翻转 true为翻转
    bool f;
    // 记录1的个数
    int cnt;
    // 能代表多少个数
    int nums;
    Bitset(int size) {
        f = false;
        cnt = 0;
        nums = size;
    }

    void fix(int idx) {
        if(!f){
            if(((arr[idx/32] >> (idx % 32)) & 1) == 0){
                arr[idx/32] |= (1 << (idx % 32));
                cnt++;
            }
        }else{
            if(((arr[idx/32] >> (idx % 32)) & 1) == 1){
                arr[idx/32] &= ~(1 << (idx % 32));
                cnt++;
            }
        }
    }
    
    void unfix(int idx) {
        if(!f){
            if(((arr[idx/32] >> (idx % 32)) & 1) == 1){
                arr[idx/32] &= ~(1 << (idx % 32));
                cnt--;
            }
        }else{
            if(((arr[idx/32] >> (idx % 32)) & 1) == 0){
                arr[idx/32] |= (1 << (idx % 32));
                cnt--;
            }
        }
    }
    
    void flip() {
        f = !f;
        cnt = nums - cnt;
    }
    
    bool all() {
        return cnt == nums;
    }
    
    bool one() {
        return cnt != 0;
    }
    
    int count() {
        return cnt;
    }
    
    string toString() {
        string s = "";
        for(int i = 0;i < (nums + 31)/32;++i){
            int cons = nums - i * 32 < 32 ? nums - i * 32 : 32;
            for(int j = 0;j < cons;++j){
                if((!f && ((arr[i] >> j) & 1) == 1) || (f && ((arr[i] >> j) & 1) == 0)){
                    s += '1';
                }else{
                    s += '0';
                }
            }
        }
        return s;
    }
};

其中,翻转使用一个变量代表,不需要每次遍历位图实现翻转。fix和unfix方法均分为翻转和未翻转两种情况。

相关推荐
融化的雪7 分钟前
reactflow整理节点,尾节点位置的大坑
开发语言·前端·javascript
豐儀麟阁贵15 分钟前
2.6 代码注释与编码规
java·开发语言
egoist202318 分钟前
[linux仓库]信号保存[进程信号·肆]
linux·开发语言·信号集·信号保存·sigpending
你不是我我24 分钟前
【Java 开发日记】什么是线程池?它的工作原理?
java·开发语言
成子不是橙子26 分钟前
Langchain | Ollama | Python快速上手使用LLM的DEMO
开发语言·python·langchain·ollama
梵得儿SHI40 分钟前
Java 反射机制深度解析:从运行时 “解剖” 类的底层逻辑
java·开发语言·反射·反射机制·private·类成员·反射的三大核心功能
熬了夜的程序员1 小时前
【LeetCode】74. 搜索二维矩阵
线性代数·算法·leetcode·职场和发展·矩阵·深度优先·动态规划
蓝色汪洋1 小时前
oj字符矩阵
算法
点云SLAM1 小时前
矩阵奇异值分解算法(SVD)的导数 / 灵敏度分析
人工智能·线性代数·算法·机器学习·矩阵·数据压缩·svd算法
坚持编程的菜鸟1 小时前
LeetCode每日一题——矩阵置0
c语言·算法·leetcode·矩阵