C++ bitset(位图)的模拟实现
文章目录
- 一、bitset接口总览
- 二、bitset模拟实现
- 1. 构造函数
- 2. set、reset、flip、test
- 3. size、count
- 4. any、none、all
- 5. 打印函数
- 三、完整代码
一、bitset接口总览
成员函数 | 功能 |
---|---|
set | 设置指定位或所有位为1(即设置为“已设置”状态) |
reset | 清空指定位或所有位,将其设为0(即设置为“未设置”状态) |
flip | 反转指定位或所有位的状态。如果位是0,则变为1;如果位是1,则变为0 |
test | 获取指定位的状态。如果位是1,则返回true;如果位是0,则返回false |
count | 获取被设置为1的位的个数(即“已设置”的位的数量) |
size | 获取位图可以容纳的位的总数。这通常指的是位图数组的总大小(以位为单位) |
any | 如果有任何一个位被设置为1(即至少有一个位是“已设置”状态),则返回true;否则返回false |
none | 如果没有位被设置为1(即所有位都是“未设置”状态),则返回true;否则返回false |
all | 如果所有位都被设置为1(即所有位都是“已设置”状态),则返回true;否则返回false |
#pragma once
#include <iostream>
#include <vector>
#include <assert.h>
namespace qi
{template <size_t N>class bitset{private:std::vector<int> _bits;public:bitset();void set(size_t pos);void reset(size_t pos);void flip(size_t pos);size_t size();size_t count();bool test(size_t pos);bool any();bool none();bool all();void Print(); //打印函数};
}
二、bitset模拟实现
1. 构造函数
在创建位图的时候我们需要根据所给的参数N,来构造一个N位的位图,并将该位图的所有位全部设置为0。
这里我们使用vector来做底层容器,一个int有32个比特位,但是非类型模板参数N,不一定总是32的整数倍,所以我们在构造的时候得先计算一下需要几个int。
例如,假如我们要建立一个50个比特位的位图,就需要两个int大小,共64个比特位,使用前50个比特位,后14个舍弃不用就好。
因此我们用式子 N/32+1
来计算需要几个int。
具体实现也比较简单,直接调用vector的resize函数即可。
bitset()
{_bits.resize(N / 32 + 1, 0);
}
2. set、reset、flip、test
set,根据下标pos,把位图的该位置设置成1
- 计算出该位位于第 i 个整数的第 j 个比特位。
- 将1左移 j 位后与第 i 个整数进行或运算即可。
void set(size_t pos)
{assert(pos < N);int i = pos / 32;//第几个整数,算出来的i相当与下标int j = pos % 32;//第i个整数的第j个比特位,j也相当于下标//注意下标都是从0开始的_bits[i] |= (1 << j);
}
reset根据下标pos,将下标为pos位置的位图设置成0
- 计算出该位位于第 i 个整数的第 j 个比特位。
- 将1左移 j 位再整体反转后与第 i 个整数进行与运算即可。
void reset(size_t pos)
{assert(pos < N);int i = pos / 32;//第几个整数,算出来的i相当与下标int j = pos % 32;//第i个整数的第j个比特位,j也相当于下标//注意下标都是从0开始的_bits[i] &= (~(1 << j));//左移后要取反在&
}
flip根据下标pos反转指定下标的比特位
- 计算出该位位于第 i 个整数的第 j 个比特位。
- 将1左移 j 位后与第 i 个整数进行异或运算即可。
void flip(size_t pos)
{assert(pos < N);int i = pos / 32;//第几个整数,算出来的i相当与下标int j = pos % 32;//第i个整数的第j个比特位,j也相当于下标//注意下标都是从0开始的_bits[i] ^= (1 << j);
}
test检测下标pos处的位图是否为1,为1返回true,否则返回false
- 计算出该位位于第 i 个整数的第 j 个比特位。
- 将1左移 j 位后与第 i 个整数进行与运算得出结果。
- 若结果非0,则该位被设置,否则该位未被设置。
bool test(size_t pos)
{assert(pos < N);int i = pos / 32;//第几个整数,算出来的i相当与下标int j = pos % 32;//第i个整数的第j个比特位,j也相当于下标//注意下标都是从0开始的if (_bits[i] & (1 << j)){return true;}return false;
}
3. size、count
size用于返回位图一共有多少位,也就是非类型模板参数N的值
直接返回N就好
size_t size()
{return N;
}
count用于计算该位图中有多少位是1,返回值是位图中1的个数
统计二进制中1的个数的方法如下:
- 将原数 n 与 n - 1 进行与运算得到新的 n 。
- 判断 n 是否为0,若 n 不为0则继续进行第一步。
- 如此进行下去,直到 n 最终为0,此时该操作进行了几次就说明二进制中有多少个1。
因为该操作每进行一次就会消去二进制中最右边的1,如图:
size_t count()
{size_t count = 0;for (auto e : _bits){int num = e;while (num){num &= (num - 1);++count;}}return count;
}
4. any、none、all
any表示位图中只要有一个1那就返回true,否则返回false
判断方式比较简单,每一个整数的所有比特位,只要有一个为1,那该整数就肯定不等于0,所以,我们可以遍历所有整数,只要有一个整数不等于0,那就说明有1,返回true,否则所有整数都是0,没一个1,返回false
bool any()
{for (auto e : _bits){if (e != 0){return true;}}return false;;
}
none表示位图中如果全是0,没一个1那就返回true,否则返回false
其实和any是对立的,如果any为假,说明全为0,说明none为真,所以none中只需将any的结果取反后返回就好了。
bool none()
{return !any();
}
all表示,全为1返回true,否则返回false
判断过程分为两步:
- 先检查前n-1个整数的二进制是否为全1。
- 再检查最后一个整数的前N%32个比特位是否为全1。
需要注意的是,如果位图没有包含最后一个整数的全部比特位,那么最后一个整数的二进制无论如何都不会为全1,所以在判断最后一个整数时应该只判断位图所包含的比特位。
bool all()
{size_t n = _bits.size();//先检查前n-1个整数for (size_t i = 0; i < n - 1; i++){if (~_bits[i] != 0) //取反后不为全0,说明取反前不为全1return false;}//再检查最后一个整数的前N%32位for (size_t j = 0; j < N % 32; j++){if ((_bits[n - 1] & (1 << j)) == 0) //该位未被设置return false;}return true;
}
5. 打印函数
为了验证bitset
对象的正确性,我们可以编写一个自定义的打印函数,该函数将遍历bitset
中的每一位,并打印出其状态(0或1)。同时,这个函数还可以统计并返回bitset
中实际被设置(即值为1)的位的数量,并将这个数量与bitset
的模板参数(即预期的大小)进行比较,以确认bitset
的大小是否符合预期。
#include <iostream>
#include <bitset>
using namespace std;// 自定义打印函数,用于打印bitset并统计1的个数
template<size_t N>
void printAndVerifyBitset(const bitset<N>& bs) {size_t count = 0; // 用于统计1的个数cout << "Bitset: ";```cpp
//打印函数
void Print()
{int count = 0;size_t n = _bits.size();//先打印前n-1个整数for (size_t i = 0; i < n - 1; i++){for (size_t j = 0; j < 32; j++){if (_bits[i] & (1 << j)) //该位被设置std::cout << "1";else //该位未被设置std::cout << "0";count++;}}//再打印最后一个整数的前N%32位for (size_t j = 0; j < N % 32; j++){if (_bits[n - 1] & (1 << j)) //该位被设置std::cout << "1";else //该位未被设置std::cout << "0";count++;}std::cout << " " << count << std::endl; //打印总共打印的位的个数
}
三、完整代码
#pragma once
#include <iostream>
#include <vector>
#include <assert.h>
namespace qi
{template <size_t N>class bitset{private:std::vector<int> _bits;public:bitset(){_bits.resize(N / 32 + 1, 0);}void set(size_t pos){assert(pos < N);int i = pos / 32;int j = pos % 32;_bits[i] |= (1 << j);}void reset(size_t pos){assert(pos < N);int i = pos / 32;int j = pos % 32;_bits[i] &= (~(1 << j));}void flip(size_t pos){assert(pos < N);int i = pos / 32;int j = pos % 32;_bits[i] ^= (1 << j);}size_t size(){return N;}size_t count(){size_t count = 0;for (auto e : _bits){int num = e;while (num){num &= (num - 1);++count;}}return count;}bool test(size_t pos){assert(pos < N);int i = pos / 32;int j = pos % 32;if (_bits[i] & (1 << j)){return true;}return false;}bool any(){for (auto e : _bits){if (e != 0){return true;}}return false;;}bool none(){return !any();}bool all(){size_t n = _bits.size();for (int i = 0; i < n - 1; i++){if (~_bits[i] != 0){return false;}}for(int i = 0; i < N % 32; i++){if ((_bits[n - 1] & (1 << i)) == 0) //该位未被设置return false;}return true;}//打印函数void Print(){int count = 0;size_t n = _bits.size();//先打印前n-1个整数for (size_t i = 0; i < n - 1; i++){for (size_t j = 0; j < 32; j++){if (_bits[i] & (1 << j)) //该位被设置std::cout << "1";else //该位未被设置std::cout << "0";count++;}}//再打印最后一个整数的前N%32位for (size_t j = 0; j < N % 32; j++){if (_bits[n - 1] & (1 << j)) //该位被设置std::cout << "1";else //该位未被设置std::cout << "0";count++;}std::cout << " " << count << std::endl; //打印总共打印的位的个数}};
}
相关文章:

C++ bitset(位图)的模拟实现
文章目录 一、bitset接口总览二、bitset模拟实现1. 构造函数2. set、reset、flip、test3. size、count4. any、none、all5. 打印函数 三、完整代码 一、bitset接口总览 成员函数功能set设置指定位或所有位为1(即设置为“已设置”状态)reset清空指定位或…...

Llama 3.2:利用开放、可定制的模型实现边缘人工智能和视觉革命
在我们发布 Llama 3.1 模型群后的两个月内,包括 405B - 第一个开放的前沿级人工智能模型在内,它们所产生的影响令我们兴奋不已。 虽然这些模型非常强大,但我们也认识到,使用它们进行构建需要大量的计算资源和专业知识。 我们也听到…...
解决R语言bug ‘sh‘ is not recognized as an internal or external command
安装源码包‘httr2’ trying URL ‘https://cran.rstudio.com/src/contrib/httr2_1.0.5.tar.gz’ Content type ‘application/x-gzip’ length 230632 bytes (225 KB) downloaded 225 KB installing source package ‘httr2’ … ** package ‘httr2’ successfully unpacked…...

记一次Mac 匪夷所思终端常用网络命令恢复记录
一天莫名奇妙发现ping dig 等基础命令都无法正常使用。还好能浏览器能正常访问,,,, 赶紧拿baidu试试^-^ ; <<>> DiG 9.10.6 <<>> baidu.com ;; global options: cmd ;; connection timed out; no serve…...
2024最新!!Java后端面试题(4)看这一篇就够了!!!!
七、异常 throw 和 throws 的区别? throw用来显式地抛出一个异常,而throws则用于在方法声明中指明该方法可能抛出的异常。简单来说,throw是抛出异常的实际动作,throws是告知调用者这个方法可能会抛出哪些异常的声明。 final、f…...

springboot整合sentinel和对feign熔断降级
一、准备 docker安装好sentinel-dashboard(sentinel控制台),参考docker安装好各个组件的命令启动sentinel-dashboard,我的虚拟机ip为192.168.200.131,sentinel-dashboard的端口为8858 二、整合sentinel的主要工作 在…...

遗传算法与深度学习实战——使用进化策略实现EvoLisa
遗传算法与深度学习实战——使用进化策略实现EvoLisa 0. 前言1. 使用进化策略实现 EvoLisa2. 运行结果相关链接 0. 前言 我们已经学习了进化策略 (Evolutionary Strategies, ES) 的基本原理,并且尝试使用 ES 解决了函数逼近问题。函数逼近是一个很好的基准问题&…...

HttpServletRequest简介
HttpServletRequest是什么? HttpServletRequest是一个接口,其父接口是ServletRequest;HttpServletRequest是Tomcat将请求报文转换封装而来的对象,在Tomcat调用service方法时传入;HttpServletRequest代表客户端发来的请…...
c++开发之编译curl(安卓版本)
为了在 Android 上编译支持 OpenSSL 的 libcurl,你需要手动编译 libcurl 和 OpenSSL,并确保它们能够在 Android 的交叉编译环境中正常工作。以下是详细的步骤说明。 1. 安装必要工具 在编译之前,确保你已经安装了以下工具: And…...

QT+ESP8266+STM32项目构建三部曲三--QT从环境配置到源程序的解析
一、阿里云环境配置 大家在编写QT连接阿里云的程序之前,先按照下面这篇文章让消息可以在阿里云上顺利流转 QTESP8266STM32项目构建三部曲二--阿里云云端处理之云产品流转-CSDN博客文章浏览阅读485次,点赞7次,收藏4次。创建两个设备ÿ…...
Web APIs 5:Window对象(BOM)+本地存储
Web APIs 5(BOM:Window对象本地存储) 1.BOM(浏览器对象模型)(后面几个对象都为BOM对象) BOM对象包含:navigator、location、document(DOM对象)、history、screenBOM是一个全局对象,即JS中的顶…...

神经网络(四):UNet图像分割网络
文章目录 一、简介二、网络结构2.1编码器部分2.2解码器部分2.3完整代码 三、实战案例 论文链接:点击跳转 一、简介 UNet网络是一种用于图像分割的卷积神经网络,其特点是采用了U型网络结构,因此称为UNet。该网络具有编码器和解码器结构&#…...
Java 编码系列:注解处理器详解与面试题解析
引言 在上一篇文章中,我们详细探讨了 Java 注解的基本概念、自定义注解、元注解等技术。本文将继续深入探讨 Java 注解处理器(Annotation Processor),介绍如何编写注解处理器,并结合大厂的最佳实践和面试题详细解析其…...

C语言 | Leetcode C语言题解之第441题排列硬币
题目: 题解: class Solution { public:int arrangeCoins(int n) {return (int) ((sqrt((long long) 8 * n 1) - 1) / 2);} };...

Linux noVNC远程桌面(xfce)部署
一、安装 VNC 服务器和桌面环境 Notebook实验 常用vnc服务 VNC (Virtual Network Computing) 是一种远程桌面协议,可以让你通过网络访问服务器的图形界面。 TurboVNC:专为图形密集型应用设计,尤其适合 3D 可视化和高分辨率图像的远程传输…...
【网络安全】身份认证
1. 身份认证 1.1 定义 身份认证(Authentication)是确认用户身份的过程,确保只有授权的用户才能访问系统或资源。它通常涉及验证用户提供的凭证,如密码、生物特征或其他识别标志。 1.2 重要性 身份认证是信息安全的第一道防线&…...

LeetCode - #124 二叉树中的最大路径和(Top 100)
文章目录 前言1. 描述2. 示例3. 答案关于我们前言 本题为 LeetCode 前 100 高频题 我们社区陆续会将顾毅(Netflix 增长黑客,《iOS 面试之道》作者,ACE 职业健身教练。)的 Swift 算法题题解整理为文字版以方便大家学习与阅读。 LeetCode 算法到目前我们已经更新到 123 期…...

Java:插入排序
目录 排序的概念 插入排序 直接插入排序 哈希排序 排序的概念 排序:所谓的排序,就是使一串记录,按照某个或某些关键字的大小,递增或递减的排列起来的操作。 稳定性:假定在待排序的记录序列中,存在多个…...

How FAR ARE WE FROM AGI?(ICLR AGI Workshop 2024)概览
关注B站可以观看更多实战教学视频:hallo128的个人空间 How FAR ARE WE FROM AGI?官网 How FAR ARE WE FROM AGI?(ICLR AGI Workshop 2024) 该研讨会将于2024年5月11日在奥地利维也纳以混合模式举行,作为 ICLR 2024年会议的一部…...
leetcode刷题day33|动态规划Part02(62.不同路径、63. 不同路径 II、 343.整数拆分、96.不同的二叉搜索树)
62.不同路径 机器人从(0 , 0) 位置出发,到(m - 1, n - 1)终点。 动规五部曲 1、确定dp数组(dp table)以及下标的含义 dp[i][j] :表示从(0 ,0)出发,到(i, j) 有dp[i][j]条不同的路…...

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...