C++实现位图

news2024/11/25 4:41:44

目录

一、什么是位图

二、位图类

1.参数及构造函数

2.set函数设置为1(代表存在) 

3.reset函数设置为0(代表不存在)

4.test函数查看状态(0还是1)

三、位图的变形


一、什么是位图

位图这个词汇比较少见,在学习位图之前,我们可以先来看看下面这个问题

40亿个数,大约为2^32。 这里有几个方案解决

解决方案1:暴力查找,将数据遍历一遍直到找到,但是40亿个数据太大了,如果换算为整数,那么至少需要16G(4*2^32代表4个字节,再乘以个数)的内存,一般的电脑是没办法开辟出这么大的连续空间的。

解决方案2:就是已经是有序了我们只需二分查找,二分查找的效率为log(n),但这里跟效率没啥关系,主要还是无法开辟这么大的内存的问题

解决方案3:将数据放到unordered_set或者set中,使用哈希表和红黑树的思想帮我们查找,但是哈希表和红黑树他们存放的结点会占用更多的空间,因为存放的结点不仅仅有数据,还有指针,因此更无法使用。

前面的几个解决方案都没有办法解决这种较大数据的问题,那么我们引入概念位图。

每一个值映射一个bit位,那么我们只需要一个bit位就可以知道该位置的值是否存在了,0为不存在,1为存在。这就叫做位图。

那么40亿个数据按照这种方法来表示他们是否存在的状态,只需要0.5G(2^32/8 代表所有的数据除以每个字节的8位)

二、位图类

其实库里面也有bitset位图,我们模仿一下这个位图的基本函数。

1.参数及构造函数

我们写出一个类,类的模板参数只有一个常量,代表我们要存放多少个数据的状态(bit位),类成员为vector<int> _bits; 

同时构造参数是给_bits开辟足够的空间,_bits.resize(N / 32 + 1, 0);   N/32代表一个整形的每一个bit位都存放状态,+1是为了防止除数把余数丢掉,比如40/32==1,如果我们只开辟一个int整形,就会将后面的数据丢掉。

template<size_t N>
class bitset
{
public:
	bitset()
	{
		_bits.resize(N / 32 + 1, 0);
	}
private:
    vector<int> _bits;
}

2.set函数设置为1(代表存在) 

 有了这个,我们如何来设置,让我们想要映射的位置变为1呢?

首先,参数部分肯定需要一个整数,代表是这个数据,我们可以通过 x/32 来表示映射到哪一个整形里了,再通过 x%32来表示映射该整形的哪一个bit位。这样我们就可以找到映射的位置了,找到位置后,应该如何将他的值赋为1呢?

我们的想法是让他无论是为0还是为1,我都要设置为1,这样初步会考虑到 |(或运算),肯定是 | 上1,我们可以通过左移运算符,将1左移到想要的位置,也就是 j 位置。这样进行或运算,就可以保证在不影响其他位置的值的前提,将想要的位置置为1了

代码如下 

void set(size_t x)
{
	int i = x / 32;
	int j = x % 32;
	_bits[i] |= (1 << j);
}

3.reset函数设置为0(代表不存在)

有了设置存在,我们也得设置不存在,如何操作呢?

一样的方法先找到 i 和 j ,无论该值为1还是为0,要想设置为0,就需要&(与运算)0才行那我们将 1 左移 j 位,再取反,就让想要的bit位变成了0,其他位都为1。

任何数(指0和1)与上1都是他本身,这样就没有改变其他bit位,任何数与上0都为0,这样就达到我们的设置改bit位为 0 目的了。

代码如下 

void reset(size_t x)
{
	int i = x / 32;
	int j = x % 32;
	_bits[i] &= ~(1 << j);
}

4.test函数查看状态(0还是1)

 有了置为1和置为0,我们还需要去查看该位置的状态。

依然是计算出  i  和  j  ,与上(1<<j)返回即可,因为任何数(指0和1)与上1都是他本身,因此我们状态为0就返回0,为1就返回1,就知道该位置的状态了。

bool test(size_t x)
{
	int i = x / 32;
	int j = x % 32;
	return _bits[i] & (1 << j);
}

测试一下,没有问题,这样就极大的节省了空间。

三、位图的变形

 如下面这个题,就是位图的变形题。

 这里也有两个方案去解决。

方案1:将单位图修改为双位图,从整形存放32个状态(0或1),编程存放16个(00或01或10)状态。由于找到只出现一次的整数,那么状态我们只需要设置(00或01或10)分别代表0个、1个、2个及以上。这个方案虽然可行,但是要重新修改位图,比较麻烦。

方案2:再写一个类,变量为两个位图,第一个位图代表状态(00或01或10)的前一个bit位,第二个位图代表状态(00或01或10)的后一个bit位

很明显,代码复用要香很多,我们选择方案2。

 set函数中,如果bit1的test(x)为false,bit2的test(x)也为false,那么就将bit2的该位置设置为1。

如果bit1的test(x)为false,bit2的test(x)为true,就将bit1位置设置为1,bit2位置设置为0,就完成了。

再写一个打印函数就完成了。

具体代码如下

template<size_t N>
class twobitset
{
public:
	void set(size_t x)
	{
		if (bit1.test(x) == false && bit2.test(x) == false)
		{
			bit2.set(x);
		}
		else if (bit1.test(x) == false && bit2.test(x) == true)
		{
			bit1.set(x);
			bit2.reset(x);
		}
	}
	void PrintOnce()
	{
		for (size_t i = 0; i < N; i++)
		{
			if (bit1.test(i) == false && bit2.test(i) == true)
			{
				cout << i << endl;
			}
		}
		cout << endl;
	}
private:
	bitset<N> bit1;
	bitset<N> bit2;
};

代码测试,没问题。

该位图只涉及到了整数的处理,如果是字符串怎么处理呢?需要用到布隆过滤器,这个我们下次再讲。

最后附上总代码

bitset.h

#pragma once
#include<vector>
namespace kky
{
	template<size_t N>
	class bitset
	{
	public:
		bitset()
		{
			_bits.resize(N / 32 + 1, 0);
		}
		void set(size_t x)
		{
			int i = x / 32;
			int j = x % 32;
			_bits[i] |= (1 << j);
		}

		void reset(size_t x)
		{
			int i = x / 32;
			int j = x % 32;
			_bits[i] &= ~(1 << j);
		}

		bool test(size_t x)
		{
			int i = x / 32;
			int j = x % 32;
			return _bits[i] & (1 << j);
		}

	private:
		vector<int> _bits;
	};
	template<size_t N>
	class twobitset
	{
	public:
		void set(size_t x)
		{
			if (bit1.test(x) == false && bit2.test(x) == false)
			{
				bit2.set(x);
			}
			else if (bit1.test(x) == false && bit2.test(x) == true)
			{
				bit1.set(x);
				bit2.reset(x);
			}
		}
		void PrintOnce()
		{
			for (size_t i = 0; i < N; i++)
			{
				if (bit1.test(i) == false && bit2.test(i) == true)
				{
					cout << i << endl;
				}
			}
			cout << endl;
		}
	private:
		bitset<N> bit1;
		bitset<N> bit2;
	};
}

 test.cpp

#include<iostream>
using namespace std;
#include"bitset.h"
//int main()
//{
//	kky::bitset<100> bs;
//	bs.set(10);
//	bs.set(11);
//	
//	cout << bs.test(9) << endl;
//	cout << bs.test(10) << endl;
//	cout << bs.test(11) << endl;
//	cout << bs.test(12) << endl;
//	bs.reset(10);
//	cout << bs.test(10) << endl;
//}




int main()
{
	int a[] = { 1,3,4,6,8,4,6,3,1,5,7 };
	kky::twobitset<100> bs;
	for (auto e : a)
	{
		bs.set(e);
	}
	bs.PrintOnce();
}

谢谢大家观看!!! 

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

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

相关文章

一行代码修复100vh bug

你知道奇怪的移动视口错误&#xff08;也称为100vh bug&#xff09;吗&#xff1f;或者如何以正确的方式创建全屏块&#xff1f; 一、100vh bug 什么是移动视口错误&#xff1f; 你是否曾经在网页上创建过全屏元素&#xff1f;只需添加一行 CSS 并不难&#xff1a; .my-page {h…

PAT 乙级 1023 组个最小数

1023 组个最小数 分数 20 作者 CAO, Peng 单位 Google 给定数字 0-9 各若干个。你可以以任意顺序排列这些数字&#xff0c;但必须全部使用。目标是使得最后得到的数尽可能小&#xff08;注意 0 不能做首位&#xff09;。例如&#xff1a;给定两个 0&#xff0c;两个 1&#xff…

【教程】cocos2dx资源加密混淆方案详解

1,加密,采用blowfish或其他 2,自定是32个字符的混淆code 3,对文件做blowfish加密,入口文件加密前将混淆code按约定格式(自定义的文件头或文件尾部)写入到文件 4,遍历资源目录,对每个文件做md5混淆,混淆原始串“相对路径”“文件名”混淆code, 文件改名并且移动到资源目录根…

Python开发GUI常用库PyQt6和PySide6介绍之二:设计师(Designer)

Python开发GUI常用库PyQt6和PySide6介绍之二&#xff1a;设计师&#xff08;Designer&#xff09; PySide6和PyQt6都有自己的设计师&#xff08;Designer&#xff09;&#xff0c;用于可视化地设计和布局GUI应用程序的界面。这些设计师提供了丰富的工具和功能&#xff0c;使开…

acwing-蓝桥杯C++ AB组辅导课Day2-递归习题+递推+二分

感谢梦翔老哥的蓝桥杯C AB组辅导课~ 递归习题&#xff1a; 1.递归实现组合型枚举 题意&#xff1a; 题目要求输出组合枚举&#xff0c;与排列不同&#xff0c;排列具有顺序之分&#xff0c;对于组合来说&#xff0c;是没有顺序之分的&#xff0c;所以[1,2,3]和[3,2,1]被看成同…

灰盒测试简要学习指南!

在本文中&#xff0c;我们将了解什么是灰盒测试、以及为什么要使用它&#xff0c;以及它的优缺点。 在软件测试中&#xff0c;灰盒测试是一种有用的技术&#xff0c;可以确保发布的软件是高性能的、安全的并满足预期用户的需求。这是一种从外部测试应用程序同时跟踪其内部操作…

【【迭代16次的CORDIC算法-verilog实现】】

迭代16次的CORDIC算法-verilog实现 -32位迭代16次verilog代码实现 CORDIC.v module cordic32#(parameter DATA_WIDTH 8d32 , // we set data widthparameter PIPELINE 5d16 // Optimize waveform)(input …

数字化时代的智能支持:亚马逊云科技轻量应用服务器技术领先

轻量应用服务器是一种简化运维、门槛低的弹性服务器&#xff0c;它的"轻"主要体现在几个方面&#xff1a;开箱即用、应用优质、上手简洁、投入划算、运维简便以及稳定可靠。相较于普通的云服务器&#xff0c;轻量应用服务器简化了云服务的操作难度、使用和管理流程&a…

全链路压测之分布式架构/SkyWalking链路追踪/中间件

最近刷题&#xff0c;学习了些压测的知识&#xff0c;大多是在小破站上的笔记&#xff0c;仅供大家参考~ 一、分布式微服务架构 微服务&#xff1a;多个系统之间相互调用 全链路&#xff1a;简单理解&#xff0c;就是一个系统调用另一个系统 二、SkyWalking链路追踪平台 链路…

阿里云经济型、通用算力型、计算型、通用型、内存型云服务器最新活动报价

阿里云作为国内领先的云计算服务提供商&#xff0c;提供了多种规格的云服务器供用户选择。为了满足不同用户的需求&#xff0c;阿里云推出了经济型、通用算力型、计算型、通用型和内存型等不同类型的云服务器。下面将详细介绍这些云服务器的最新活动报价。 一、阿里云特惠云服…

01-Go语言介绍以及win环境搭建

1、Go 语言介绍 Go 即 Golang&#xff0c;是 Google 公司 2009 年 11 月正式对外公开的一门编程语言。 根据 Go 语言开发者自述&#xff0c;近 10 多年&#xff0c;从单机时代的 C 语言到现在互联网时代的 Java&#xff0c; 都没有令人满意的开发语言&#xff0c;而 C往往给人…

python学习笔记--异常捕获

异常场景 numinput("input you number:") n9000 try:resultn/int(num)print({} 除以num 结果为{}.format(n,result)) except ZeroDivisionError as err:print("0不可以作为除数&#xff0c;出现报错{}".format(err)) except ValueError as err:print(&quo…

【超图】SuperMap iClient3D for WebGL/WebGPU ——地形影像

作者&#xff1a;taco 号外&#xff01;号外&#xff01;开新坑了&#xff01;开新坑了&#xff01;对于一个代码小白来讲&#xff0c;设置可能是刚接触开发的人&#xff08;还没接触准备接触&#xff09;的人来说。对于读代码或是在对产品的使用上会存在许许多多的疑惑。接下来…

Mybatis基本操作

目录 准备工作 删除操作 预编译SQL 增加操作 获取返回的主键 更新操作 准备工作 准备数据库表 emp创建一个新的springboot工程&#xff0c;选择引入对应的起步依赖&#xff08;mybatis、mysql驱动、lombok&#xff09;application.properties中引入数据库连接信息创建对应…

HTML+CSS做一个冰立方体时钟

文章目录 💕效果展示💕代码展示HTMLJS💕效果展示 💕代码展示 HTML <!DOCTYPE html> <html lang

Python简介:一种强大的编程语言

Python是一种高级、通用的编程语言&#xff0c;以其简洁易读的语法和强大的功能而闻名。它广泛应用于各种领域&#xff0c;包括软件开发、数据分析、人工智能等。本文将详细介绍Python的特点、应用领域以及如何开始学习Python。 &#xfeff; &#xfeff;一、Python的特点 1…

vue2 el-table 行按钮过多,按钮超出指定个数,显示为下拉菜单(简单的自定义组件)01

vue2 el-table 行按钮过多&#xff0c;按钮超出指定个数&#xff0c;显示为下拉菜单&#xff08;简单的自定义组件01&#xff09; 上图 优化前 按钮太多不美观 优化后 默认展示三个按钮 超出显示下拉菜单 上代码 封装按钮组件 OperateBtn.vue // OperateBtn.vue<templ…

mysql中的事务和索引

1. 索引 索引&#xff1a;index&#xff08;下标&#xff09;->目录 索引是一种特殊的文件&#xff0c;包含着对数据表里所有记录的引用指针。可以对表中的一列或多列创建索引&#xff0c; 并指定索引的类型&#xff0c;各类索引有各自的数据结构实现。 1.1 索引的作用 MyS…

MVVM响应式

聚沙成塔每天进步一点点 本文内容 ⭐ 专栏简介MVVM响应式1. 什么是MVVM模式?2. Vue中的响应式数据3. 数据绑定与视图更新⭐ 写在最后⭐ 专栏简介 Vue学习之旅的奇妙世界 欢迎大家来到 Vue 技能树参考资料专栏!创建这个专栏的初衷是为了帮助大家更好地应对 Vue.js 技能树的学习…

SWUST-会理财的小明

一波操作之后我发现我在乱写&#xff0c;发现原来利息是这样算的 然后我一波操作之后发现也不是这样算的。原来利息是这样算的 原来是幂运算&#xff01; 什么东西。。。 原来总金额就是本金*&#xff08;1利率&#xff09;^年限。利息就是总金额-本金&#xff01;&#xff01…