算法【位图】

特别提醒: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方法均分为翻转和未翻转两种情况。

相关推荐
2301_7938046910 小时前
模板代码安全性增强
开发语言·c++·算法
测试_AI_一辰10 小时前
Agent & RAG 测试工程笔记 13:RAG检索层原理拆解:从“看不懂”到手算召回过程
人工智能·笔记·功能测试·算法·ai·ai编程
干啥啥不行,秃头第一名10 小时前
C++中的观察者模式
开发语言·c++·算法
SuperEugene10 小时前
Vue3 + Vue Router + Pinia 路由守卫规范:beforeEach 应做 / 不应做,避死循环、防重复请求|状态管理与路由规范篇
开发语言·前端·javascript·vue.js·前端框架
桃气媛媛10 小时前
python流程控制-匹配语句match
开发语言·python
阿Y加油吧10 小时前
力扣打卡——反转链表、回文链表判断 题解
算法·leetcode
羊小猪~~10 小时前
算法/力扣--数组典型题目
c语言·c++·python·算法·leetcode·职场和发展·求职招聘
Johnny.Cheung10 小时前
【德国技术面试】两道小算法题(求两数之和/解谜游戏)
算法·面试
东东__net10 小时前
js逆向与谷歌加密库
开发语言·前端·javascript
x_xbx10 小时前
LeetCode:198. 打家劫舍
算法·leetcode·职场和发展