<C++> STL_bitset使用和模拟实现

bitset的介绍

位图的引入

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

要判断一个数是否在某一堆数中,我们可能会想到如下方法:

  • 将这一堆数进行排序,然后通过二分查找的方法判断该数是否在这一堆数中。
  • 将这一堆数插入到unordered_set容器中,然后调用find函数判断该数是否在这一堆数中。

单从方法上来看,这两种方法都是可以,而且效率也不错,第一种方法的时间复杂度是O(N*LogN),第二种方法的时间复杂度是O(N)。

但问题是这里有40亿个数,若是我们要将这些数全部加载到内存当中,那么将会占用16G的空间,空间消耗是很大的。因此从空间消耗来看,上面这两种方法实际都是不可行的。

位图解决

实际在这个问题当中,我们只需要判断一个数在或是不在,即只有两种状态,那么我们可以用一个比特位来表示数据是否存在,如果比特位为1则表示存在,比特位为0则表示不存在。比如:

在这里插入图片描述

无符号整数总共有2的32次方个,因此记录这些数字就需要2的32次方个比特位,也就是512M的内存空间,内存消耗大大减少。

位图的概念

所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。

位图的应用

常见位图的应用如下:

  1. 快速查找某个数据是否在一个集合中。
  2. 排序。
  3. 求两个集合的交集、并集等。
  4. 操作系统中磁盘块标记。
  5. 内核中信号标志位(信号屏蔽字和未决信号集)。

bitset的使用

bitset是一种特殊的容器类,它的每个元素只占用一位,只能为0或1。

bitset的定义方式

创建bitset对象,你可以直接指定它的大小,或者使用一个无符号长整型数或字符串初始化它:

方式一: 构造一个16位的位图,所有位都初始化为0。

bitset<16> bs1; //0000000000000000

方式二: 构造一个16位的位图,根据所给值初始化位图的前n位。

bitset<16> bs2(0xfa5); //0000111110100101

方式三: 构造一个16位的位图,根据字符串中的0/1序列初始化位图的前n位。

bitset<16> bs3(string("10111001")); //0000000010111001

bitset成员函数的使用

bitset类提供了以下一些常用的成员函数:

  • set:将bitset中的一位或所有位设置为1
  • reset:将bitset中的一位或所有位设置为0
  • flip:翻转bitset中的一位或所有位
  • test:获取指定位的状态
  • count:返回bitset中1的个数
  • size:返回bitset中位的个数
  • any:如果bitset中至少有一位为1,则返回true
  • none:如果bitset中所有位都为0,则返回true
  • all:如果所有位都被设置,则返回true
  • to_string:将bitset转换为字符串
  • to_ulongto_ullong:将bitset转换为无符号(长)整型数

使用示例:

#include <bitset>
#include <iostream>
using namespace std;int main() {bitset<8> bs;//位置从0开始bs.set(2);         //设置第2位bs.set(4);         //设置第4位cout << bs << endl;//00010100bs.flip();                 //反转所有位cout << bs << endl;        //11101011cout << bs.count() << endl;//6cout << bs.test(3) << endl;//1  3的位置为1bs.reset(0);       //清空第0位,第0位变成了0cout << bs << endl;//11101010bs.flip(7);        //反转第7位cout << bs << endl;//01101010cout << bs.size() << endl;//8cout << bs.any() << endl;//任何位为1,就返回true 1bs.reset();               //清空所有位cout << bs.none() << endl;//没有位被设置,就返回true 1bs.set();                //设置所有位cout << bs.all() << endl;//所有位被设置,返回true  1return 0;
}22211

注意: 使用成员函数set、reset、flip时,若指定了某一位则操作该位,若未指定位则操作所有位。

bitset运算符的使用

一、bitset中>>、<<运算符的使用。
bitset容器对>>、<<运算符进行了重载,我们可以直接使用>>、<<运算符对biset容器定义出来的对象进行输入输出操作。

#include <iostream>
#include <bitset>
using namespace std;int main() {bitset<8> bs;cin >> bs;         //10110cout << bs << endl;//00010110return 0;
}

二、bitset中赋值运算符、关系运算符、复合赋值运算符、单目运算符的使用。

bitset容器中不仅对赋值运算符和一些关系运算符进行了重载,而且对一些复合赋值运算符和单目运算符也进行了重载,我们可以直接使用这些运算符对各个位图进行操作。

包括如下运算符:

  • 赋值运算符:=。
  • 关系运算符:==、!=。
  • 复合赋值运算符:&=、|=、^=、<<=、>>=。
  • 单目运算符:~。

三、bitset中位运算符的使用。
bitset容器中同时也对三个位运算符进行了重载,我们可以直接使用&、|、^对各个位图进行操作。

#include <bitset>
#include <iostream>
#include <string>
using namespace std;int main() {bitset<8> bs1(string("10101010"));bitset<8> bs2(string("01010101"));cout << (bs1 & bs2) << endl;//00000000cout << (bs1 | bs2) << endl;//11111111cout << (bs1 ^ bs2) << endl;//11111111return 0;
}

四、bitset中[ ]运算符的使用。
bitset容器中对[ ]运算符进行了重载,我们可以直接使用[ ]对指定位进行访问或修改。

#include <bitset>
#include <iostream>
#include <string>
using namespace std;int main() {bitset<8> bs(string("00110101"));cout << bs[0] << endl;//1bs[0] = 0;cout << bs << endl;//00110100return 0;
}

bitset的模拟实现

bit类各函数接口总览

namespace cl {//模拟实现位图template<size_t N>class bitset {public://构造函数bitset();//设置位void set(size_t pos);//清空位void reset(size_t pos);//反转位void flip(size_t pos);//获取位的状态bool test(size_t pos);//获取可以容纳的位的个数size_t size();//获取被设置位的个数size_t count();//判断位图中是否有位被设置bool any();//判断位图中是否全部位都没有被设置bool none();//判断位图中是否全部位都被设置bool all();//打印函数void Print();private:vector<int> _bits;//位图};
}// namespace cl

bitset类的实现

构造函数

在构造位图时,我们需要根据所给位数N,创建一个N位的位图,并且将该位图中的所有位都初始化为0。

一个整型有32个比特位,因此N个位的位图就需要用到N/32个整型,但是实际我们所需的整型个数是N/32+1,因为所给非类型模板参数N的值可能并不是32的整数倍。

例如,当N为40时,我们需要用到两个整型,即40/32+1=2。

在这里插入图片描述

代码如下:

//构造函数
bitset(){_bits.resize(N / 32 + 1, 0);
}

成员函数

set

set成员函数用于设置位。

设置位图中指定的位的方法如下:

  1. 计算出该位位于第 i 个整数的第 j 个比特位。

  2. 将1左移 j 位后与第 i 个整数进行或运算即可。

    在这里插入图片描述

代码如下

//设置位
void set(size_t pos){assert(pos < N);//算出pos映射的位在第i个整数的第j个位int i = pos / 32;int j = pos % 32;_bits[i] |= (1 << j); //将该位设置为1(不影响其他位)
}
reset

reset成员函数用于清空位。

清空位图中指定的位的方法如下:

  1. 计算出该位位于第 i 个整数的第 j 个比特位。
  2. 将1左移 j 位再整体反转后与第 i 个整数进行与运算即可。

在这里插入图片描述

代码如下

//清空位
void reset(size_t pos){assert(pos < N);//算出pos映射的位在第i个整数的第j个位int i = pos / 32;int j = pos % 32;_bits[i] &= (~(1 << j)); //将该位设置为0(不影响其他位)1进行翻转后只有第j位置位0 其他都是1,所以只会影响第j位置的数字
}
flip

flip成员函数用于反转位。

反转位图中指定的位的方法如下:

  1. 计算出该位位于第 i 个整数的第 j 个比特位。
  2. 将1左移 j 位后与第 i 个整数进行异或运算即可。

在这里插入图片描述

代码如下

//反转位
void flip(size_t pos){assert(pos < N);//算出pos映射的位在第i个整数的第j个位int i = pos / 32;int j = pos % 32;_bits[i] ^= (1 << j); //将该进行反转(不影响其他位)
}
test

test成员函数用于获取位的状态。

获取位图中指定的位的状态的方法如下:

  1. 计算出该位位于第 i 个整数的第 j 个比特位。
  2. 将1左移 j 位后与第 i 个整数进行与运算得出结果。
  3. 若结果非0,则该位被设置,否则该位未被设置。

在这里插入图片描述

代码如下

//获取位的状态
bool test(size_t pos){assert(pos < N);//算出pos映射的位在第i个整数的第j个位int i = pos / 32;int j = pos % 32;//该比特位被设置if (_bits[i] & (1 << j)){return true;} else {//该比特位未被设置return false;} 
}
size、count

size成员函数用于获取位图中可以容纳的位的个数。

我们直接将所给非类型模板参数进行返回即可。

//获取可以容纳的位的个数
size_t size(){return N;
}

count成员函数用于获取位图中被设置的位的个数。

获取位图中被设置的位的个数,也就是统计位图中1的个数,我们只需要依次统计每个整数二进制中1的个数,然后将其相加即可得到位图中1的个数。

统计二进制中1的个数的方法如下:

  1. 将原数n与n - 1进行与运算得到新的 n 。
  2. 判断n是否为0,若 n 不为0则继续进行第一步。

如此进行下去,直到n最终为0,此时该操作进行了几次就说明二进制中有多少个1。

因为该操作每进行一次就会消去二进制中最右边的1

代码如下

//获取被设置位的个数
size_t count(){size_t count = 0;//将每个整数中1的个数累加起来for (auto e : _bits){int num = e;//计算整数num中1的个数while (num){num = num&(num - 1);count++;}}return count; //位图中1的个数,即被设置位的个数
}
any、none、all

any成员函数用于判断位图中是否有位被设置。

我们只需遍历每一个整数,若这些整数全部都为0,则说明位图中没有位被设置过。
虽然位图可能并没有包含最后一个整数的全部比特位,但由于我们构造位图时是将整数的全部比特位都初始化成了0,因此不会对此处判断造成影响。

在这里插入图片描述

代码如下

//判断位图中是否有位被设置
bool any(){//遍历每个整数for (auto e : _bits){//该整数中有位被设置if (e != 0) {return true;}		}return false; //全部整数都是0,则没有位被设置过
}

none成员函数用于判断位图中是否全部位都没有被设置。

位图中是否全部位都没有被设置,实际上就是位图中有位被设置的反面,因此none成员函数直接调用any成员函数,然后将返回值取反后再进行返回即可。

//判断位图中是否全部位都没有被设置
bool none() {return !any();
}

all成员函数用于判断位图中是否全部位都被设置。

判断过程分为两步:

  1. 先检查前n-1个整数的二进制是否为全1。
  2. 再检查最后一个整数的前N%32个比特位是否为全1。

需要注意的是,如果位图没有包含最后一个整数的全部比特位,那么最后一个整数的二进制无论如何都不会为全1,所以在判断最后一个整数时应该只判断位图所包含的比特位。

在这里插入图片描述

代码如下

//判断位图中是否全部位都被设置
bool all() {size_t n = _bits.size();//先检查前n-1个整数for (size_t i = 0; i < n - 1; i++) {//取反后不为全0,说明取反前不为全1if (~_bits[i] != 0){return false;}}//再检查最后一个整数的前N%32位for (size_t j = 0; j < N % 32; j++) {//等于0表示没有被设置if ((_bits[n - 1] & (1 << j)) == 0){return false;}}return true;
}

打印函数

可以实现一个打印函数,便于检查我们上述代码的正确性,打印位图时遍历位图所包含的比特位进行打印即可,在打印位图的过程中可以顺便统计位图中位的个数count,将count与我们传入的非类型模板参数N进行比较,可以判断位图大小是否是符合我们的预期。

//打印函数
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)) {cout << "1";} else {cout << "0";}count++;}}//再打印最后一个整数的前N%32位for (size_t j = 0; j < N % 32; j++) {if (_bits[n - 1] & (1 << j)){cout << "1";}else{cout << "0";}count++;}cout << " " << count << endl;//打印总共打印的位的个数
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/93360.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【python海洋专题九】Cartopy画地形等深线图

【python海洋专题九】Cartopy画地形等深线图 水深图基础差不多了&#xff0c;可以换成温度、盐度等 本期加上等深线 本期内容 1&#xff1a;地形等深线 cf ax.contour(lon, lat, ele[:, :], levelsnp.linspace(-9000,-100,10),colorsgray, linestyles-,linewidths0.25, t…

用python表格初级尝试

Excel&#xff0c;我的野心 当我输入3,2 就表示在第3行第2列。的单元格输入数据input输入表头 &#xff08;input内除了/&#xff0c;空格 回车 标点符号等 全部作为单元格分隔符&#xff09;由我设置input输入的是行or列 给选项 1. 行 2. 列默认回车或没输入值是列由我设置起…

数据结构 B树 B+树 B*树 特性与规则说明 图解

文章目录 前言B树基本规则B树的数据插入&#xff08;文字描述图解&#xff09;B树数据查找B树效率分析B树的作用B树基本规则B树 与 B树对比B*树基本规则B*树 与 B树对比拓展 前言 B树基本规则 每个节点最多有m个子节点&#xff0c;其中m是一个正整数。根节点除外&#xff0c;其…

聊聊并发编程——线程池

目录 Java线程池 处理流程 线程池主要参数 常见的拒绝策略 execute和submit区别 关闭线程池 常见的线程池 newSingleThreadExecutor newFixedThreadPool newCachedThreadPool newScheduledThreadPool 线程池的状态 Java线程池 运用场景最多的并发框架&#xff0c;…

阿里巴巴K8S集成seata

正文 在K8S集成seata&#xff0c;官方配置 代码 apiVersion: v1 kind: Service metadata:name: seata-servernamespace: wmz-devlabels:k8s-app: seata-server spec:type: NodePortports:- port: 8091nodePort: 30091protocol: TCPname: httpselector:k8s-app: seata-server-…

Java练习题-键盘录入字符串实现大小写转换

✅作者简介&#xff1a;CSDN内容合伙人、阿里云专家博主、51CTO专家博主、新星计划第三季python赛道Top1&#x1f3c6; &#x1f4c3;个人主页&#xff1a;hacker707的csdn博客 &#x1f525;系列专栏&#xff1a;Java练习题 &#x1f4ac;个人格言&#xff1a;不断的翻越一座又…

idea清空缓存类

解决办法 网上有很多是让你去清空什么maven依赖&#xff0c;但假如这个项目是你不可以大刀阔斧的话 可以清空idea缓存 选择 Invalidate 开头的 然后全选 运行重启idea OK

Linux系统编程系列之线程

一、什么是线程 线程&#xff08;Thread&#xff09;是计算机中的基本执行单元&#xff0c;是操作系统调度的最小单位。线程是进程内的一个独立执行流程&#xff0c;一个进程可以包含多个线程&#xff0c;这些线程共享进程的资源&#xff0c;但每个线程都有自己的独立栈空间以及…

Java后端模拟面试,题集①

1.Spring bean的生命周期 实例化 Instantiation属性赋值 Populate初始化 Initialization销毁 Destruction 2.Spring AOP的创建在bean的哪个时期进行的 &#xff08;图片转载自Spring Bean的完整生命周期&#xff08;带流程图&#xff0c;好记&#xff09;&#xff09; 3.MQ如…

基于SSM的选课排课系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用Vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

2023年中国奶牛平均单产量、奶类产量及发展趋势分析:液态奶市场向高端化发展[图]

2022年&#xff0c;我国奶产业素质稳步提升&#xff0c;全国存栏百头以上规模养殖比例达到72%&#xff0c;同比提高2个百分点。奶牛平均单产9.2吨&#xff0c;较2021年增加500千克&#xff1b;规模牧场95%以上配备全混合日粮搅拌车&#xff0c;原料奶生产100%实现机械化挤奶&am…

3D WEB轻量化引擎HOOPS助力3D测量应用蓬勃发展:效率、精度显著提升

在3D开发工具领域&#xff0c;Tech Soft 3D打造的HOOPS SDK已经崭露头角&#xff0c;成为了全球领先的3D领域开发工具提供商。HOOPS SDK包括四种不同的3D软件开发工具&#xff0c;已成为行业的翘楚。 其中&#xff0c;HOOPS Exchange以其CAD数据转换的能力脱颖而出&#xff0c…

如何破解压缩包zip解压密码?

Zip压缩包设置了密码&#xff0c;解压的时候就需要输入正确对密码才能顺利解压出文件&#xff0c;正常当我们解压文件或者删除密码的时候&#xff0c;虽然方法多&#xff0c;但是都需要输入正确的密码才能完成。忘记密码就无法进行操作。 那么&#xff0c;忘记了zip压缩包的密…

leetCode 55.跳跃游戏 贪心算法

给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入…

基于微信小程序的手机在线商城小程序设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言系统主要功能&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计…

MySql017——组合查询

一、UNION作用 可用UNION操作符来组合数条SQL查询。 二、UNION 使用规则 1、UNION的使用很简单。所需做的只是给出每条SELECT语句&#xff0c;在各条语句之间放上关键字UNION。2、UNION必须由两条或两条以上的SELECT语句组成&#xff0c;语句之间用关键字UNION分隔&#xff…

专业图像处理软件DxO PhotoLab 7 mac中文特点和功能

DxO PhotoLab 7 mac是一款专业的图像处理软件&#xff0c;它为摄影师和摄影爱好者提供了强大而全面的照片处理和编辑功能。 DxO PhotoLab 7 mac软件特点和功能 强大的RAW和JPEG格式处理能力&#xff1a;DxO PhotoLab 7可以处理来自各种相机的RAW格式图像&#xff0c;包括佳能、…

(SAR)Sentinel-1影像自动下载

基于ASF网站提供的python代码&#xff0c;实现Sentinel-1影像的自动下载&#xff1b; 1、登录ASF网站 登录Sentinel-1影像ASF网站&#xff1a;https://search.asf.alaska.edu/&#xff1b; 点击网站最右侧Sign in图标&#xff0c;进行用户注册&#xff1b; 注册完用户之后&…

【c语言】推箱子

所需知识&#xff1a;c语言枚举&#xff0c;数组&#xff0c;for循环&#xff0c;while循环&#xff0c;switch,case语句&#xff0c;图形库相关函数 1.调整控制台窗口大小 #define _CRT_SECURE_NO_WARNINGS #include <stdlib.h>#include <stdio.h> int main() {…

STM32之DMA

简介 • DMA &#xff08; Direct Memory Access &#xff09;直接存储器存取 &#xff08;可以直接访问STM32内部存储器&#xff0c;如SRAM、程序存储器Flash和寄存器等&#xff09; •DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输&#xff0c;无须CPU干预&a…