位图(C语言版)

文章目录

  • 位图
    • 模型
    • 基本操作
    • 实现
      • 代码
      • 运行结果
    • 应用
      • 存储只有两种状态的数据
      • 排序并去重

位图

模型

位图是“位”的数组。

为什么需要构建一个专门的数据结构来表示位的数组?:因为计算机最小的寻址单位是字节,而不是位。

位图是一种内存紧凑的数据结构,即位图在内存资源紧张的设备中更多使用,如嵌入式设备等。

基本操作

  • 增 set:将某一位设置为1
  • 删 unset:将某一位设置为0
  • 查 isset:判断某一位是不是1
  • 遍历

实现

代码

// 位运算技巧
n * 32 等价于 n << 5   /* 2^5 = 32 */
n / 32 等价于 n >> 5
n % 32 等价于 n & 0x1F /* 与31(MASK)相与 */
n |= 0x1 << offset    /* 将n的第offset位设置为1 */
n &= ~(0x1 << offset) /* 将n的第offset位设置为0 */

位图要求尽可能少地使用内存空间,所以实际使用多少内存,就申请多少内存。

// BitMap.h
#include <stdbool.h>
#include <stdint.h>typedef uint32_t Word;   // uint32_t: 1.大小确定 (32bits);  2.无符号整数typedef struct {Word* array;   size_t bits;	    // 位数组的长度
} BitMap;BitMap* bitmap_create(size_t bits);
void bitmap_destroy(BitMap* bm);void bitmap_set(BitMap* bm, size_t n);	// n is a bit index
void bitmap_unset(BitMap* bm, size_t n);
bool bitmap_isset(BitMap* bm, size_t n);
void bitmap_clear(BitMap* bm);
// BitMap.c
#include "BitMap.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>#define BITS_PER_WORD 32
#define BITMAP_SHIFT 5
#define BITMAP_MASK 0x1F
// 存储bits位,需要多少个word
#define BITMAP_SIZE(bits) ((bits + BITS_PER_WORD - 1) >> BITMAP_SHIFT)// 位图的长度
BitMap* bitmap_create(size_t bits) {BitMap* bm = (BitMap*)malloc(sizeof(BitMap));bm->array = (Word*)calloc(BITMAP_SIZE(bits), BITS_PER_WORD);bm->bits = bits;return bm;
}void bitmap_destroy(BitMap* bm) {// 1. 释放arrayfree(bm->array);// 2. 释放BitMapfree(bm);
}void grow_capacity(BitMap* bm, size_t bits) {Word* newArray = (Word*)realloc(bm->array, BITMAP_SIZE(bits) * sizeof(Word));bm->array = newArray;// realloc 并不会自动将扩容的空间清零,需要手动清零int bytes = (BITMAP_SIZE(bits) - BITMAP_SIZE(bm->bits)) * sizeof(Word);memset(bm->array + BITMAP_SIZE(bm->bits), 0, bytes);
}void bitmap_set(BitMap* bm, size_t n) {	// n is a bit index// 需判断索引n是否在位图范围内if (n + 1 > bm->bits) { // 这里不一定要扩容,但要改变bm->bitsif (BITMAP_SIZE(n + 1) > BITMAP_SIZE(bm->bits)) { // 不在BitMap范围内,需扩容// 扩容grow_capacity(bm, n + 1);}bm->bits = n + 1;}// 如何表示第n位(word, offset)size_t word = n >> BITMAP_SHIFT; // 该索引在哪个word块中size_t offset = n & BITMAP_MASK; // 等价于 n % BITS_PER_WORD (取余操作),表示该索引在该word块的offset位bm->array[word] |= (0x01 << offset);
}void bitmap_unset(BitMap* bm, size_t n) {// 需判断索引n是否在位图范围内if (n + 1 > bm->bits) { // 这里不一定要扩容,但要改变bm->bitsif (BITMAP_SIZE(n + 1) > BITMAP_SIZE(bm->bits)) { // 不在BitMap范围内,需扩容// 扩容grow_capacity(bm, n + 1);}bm->bits = n + 1;}// 如何表示第n位(word, offset)size_t word = n >> BITMAP_SHIFT; // 该索引在哪个word块中size_t offset = n & BITMAP_MASK; // 等价于 n % BITS_PER_WORD (取余操作),表示该索引在该word块的offset位bm->array[word] &= ~(0x01 << offset);
}bool bitmap_isset(BitMap* bm, size_t n) {// 需判断索引n是否在位图范围内if (n + 1 > bm->bits) {printf("索引n不在位图范围内\n");return false;}// 如何表示第n位(word, offset)size_t word = n >> BITMAP_SHIFT; // 该索引在哪个word块中size_t offset = n & BITMAP_MASK; // 等价于 n % BITS_PER_WORD (取余操作),表示该索引在该word块的offset位return bm->array[word] & (0x1 << offset);
}void bitmap_clear(BitMap* bm) {// 全部置零int bytes = BITMAP_SIZE(bm->bits) * sizeof(Word);memset(bm->array, 0, bytes);
}
// main.c
#include <stdio.h>
#include "BitMap.h"int main() {BitMap* bm = bitmap_create(100);int bits_per_word = 8 * sizeof(bm->array[0]);printf("位图使用了%d 位的空间,实际开辟的内存空间%d\n", bm->bits, bits_per_word * (1 + bm->bits / bits_per_word));bitmap_set(bm, 80);printf("第80位的值为:%d\n", bitmap_isset(bm, 80));bitmap_unset(bm, 80);printf("第80位的值为:%d\n", bitmap_isset(bm, 80));bitmap_set(bm, 130); // 扩容printf("位图使用了%d 位空间,实际开辟的内存空间%d\n", bm->bits, bits_per_word * (1 + bm->bits / bits_per_word));bitmap_clear(bm);printf("第0~50位的值为:");for (int i = 0; i < 50; i++) {printf(" %d", bitmap_isset(bm, i));}bitmap_destroy(bm);return 0;
}

运行结果

在这里插入图片描述

应用

存储只有两种状态的数据

比如,存储10亿 ( 1 0 9 ≈ 2 30 10^{9} \approx 2^{30} 109230) QQ用户的在线状态:

  • 如果用int数组存储,那么需要的存储空间为:4B * 2^30 = 4GB
  • 如果用位图存储,所需空间位:1/8B * 2^30 = 0.125GB

排序并去重

比如排序数组int array[] = {123, 456, 3, 7, 8, 9, 34, 3, 7, 3};并去重

  • 如果用平常方法:需要先对数组进行排序(快速排序O(nlog(n)),然后对排序后的数组去重 (O(n))
  • 使用位图,可以将数组array的值对应为位图的位索引, 比如数组第一个值为123, 那么就将位图索引为128的位设置为1,对其他数组值也进行相同处理。最后输出位图中值为1的位对应索引即可。时间复杂度为O(n)

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

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

相关文章

AI写代码工具时代:前端开发技能迭代的挑战与应对

近年来&#xff0c;人工智能&#xff08;AI&#xff09;技术飞速发展&#xff0c;深刻地改变着各个行业&#xff0c;前端开发领域也不例外。AI技术不仅带来了新的开发模式&#xff0c;也显著加快了前端开发技能的迭代速度&#xff0c;给前端工程师带来了巨大的挑战。本文将深入…

文件上传功能(四)——项目集成

总说 过程参考黑马程序员SpringBoot3Vue3全套视频教程&#xff0c;springbootvue企业级全栈开发从基础、实战到面试一套通关_哔哩哔哩_bilibili 目录 总说 一、功能实现 1.1 Controller层 1.2 测试接口 一、功能实现 我们要将入门程序改为一个工具类 在utils目录下创建A…

使用 GPT-SoVITS 克隆声音,很详细

使用 GPT-SoVITS 克隆声音&#xff0c;很详细 一、前言二、下载三、启动四、克隆声音1、准备克隆音频2、分离人声伴奏3、音频分割4、语音降噪5、ASR工具6、语音文本校对标注工具7、训练模型8、微调训练9、推理 一、前言 最近对文本转语言很感兴趣&#xff0c;但对直接在网站上…

STM32+Proteus+DS18B20数码管仿真实验

1. 实验准备 硬件方面&#xff1a; 了解 STM32 单片机的基本原理和使用方法&#xff0c;本实验可选用常见的 STM32F103 系列。熟悉 DS18B20 温度传感器的工作原理和通信协议&#xff08;单总线协议&#xff09;。数码管可选用共阴极或共阳极数码管&#xff0c;用于显示温度值。…

【银河麒麟高级服务器操作系统】服务器卡死后恢复系统日志丢失-分析及处理全过程

了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;https://product.kylinos.cn 开发者专区&#xff1a;https://developer.kylinos.cn 文档中心&#xff1a;https://document.kylinos.cn 服务器环境以及配置 【机型】 处理器&#xff…

【linux】在 Linux 上部署 DeepSeek-r1:32/70b:解决下载中断问题

【linux】在 Linux 上部署 DeepSeek-r1:32/70b:解决下载中断问题 【承接商业广告,如需商业合作请+v17740568442】 文章目录 【linux】在 Linux 上部署 DeepSeek-r1:32/70b:解决下载中断问题问题描述:解决方法方法一:手动中断并重启下载方法二:使用 Bash 脚本自动化下载在…

Java(api中常用类,包括Object类,Arrays类,String类,基本数据类型包装类)

目录 一.api 1.api介绍: 二.Object类 1.toString方法 2.equals方法 1.什么是equals方法 2.Object类向我们提供的equals方法 ​编辑 3.equals方法与""的区别 三.Arrays类 1.toString方法 2.sort方法 3.copyOf方法 4.fill方法 5.binarySearch方法 四.基…

物联网行业通识:从入门到深度解析

物联网行业通识&#xff1a;从入门到深度解析 &#xff08;图1&#xff1a;物联网生态示意图&#xff09; 一、引言&#xff1a;万物互联时代的到来 根据IDC最新预测&#xff0c;到2025年全球物联网设备连接数将突破410亿&#xff0c;市场规模达1.1万亿美元。物联网&#xff…

python语言进阶之函数

目录 前言 函数的创建和调用 函数创建 调用函数 参数传递 形式参数和实际参数 位置参数 数量必须与定义时一致 位置必须与定义时一致 关键字参数 为参数设置默认值 可变参数 **parameter 返回值 变量的作用域 局部变量 全局变量 匿名函数 前言 提到函数&…

Qt信号槽调用出错:Qt: Dead lock detected while activating a BlockingQueuedConnection

目录 1.现象和原因分析 2. 总结 1.现象和原因分析 就在最近的开发过程中&#xff0c;程序一运行在控制台就打印&#xff1a; Qt: Dead lock detected while activating a BlockingQueuedConnection&#xff1a; 咋一看&#xff0c;怎么出现死锁了呢&#xff1f;仔细看下…

Linux安装Minio

1、下载rpm包 2、rpm 安装 rpm -ivh xx.rpm3、通过查看minion状态&#xff0c;查看其配置文件位置 systemctl start minio可以根据情况自定义修改配置文件内容&#xff0c;这里暂时不做修改 4、创建数据文件和日志文件&#xff0c;一般在/usr/local/ 5、编写启动脚本 #!/bi…

计算四个锚点TOA定位中GDOP的详细步骤和MATLAB例程

该MATLAB代码演示了在三维空间中,使用四个锚点的TOA(到达时间)定位技术计算几何精度衰减因子(GDOP)的过程。如需帮助,或有导航、定位滤波相关的代码定制需求,请联系作者 文章目录 DOP计算原理MATLAB例程运行结果示例关键点说明扩展方向另有文章: 多锚点Wi-Fi定位和基站…

基于Spring Boot+Vue的宠物服务管理系统(源码+文档)

项目简介 宠物服务管理系统实现了以下功能&#xff1a; 基于Spring BootVue的宠物服务管理系统的主要使用者分为用户管理模块&#xff0c;由于系统运行在互联网络中&#xff0c;一些游客或者病毒恶意进行注册&#xff0c;产生大量的垃圾用户信息&#xff0c;管理员可以对这些…

jenkins服务启动-排错

服务状态为active (exited) 且进程不在 查看/etc/rc.d/init.d/jenkins配置 获取配置参数 [rootfy-jenkins-prod jenkins]# cat /etc/rc.d/init.d/jenkins | grep -v #JENKINS_WAR"/usr/lib/jenkins/jenkins.war" test -r "$JENKINS_WAR" || { echo "…

vue3 分析总结响应式丢失问题原因(二)

上一篇文件理解了响应式对象应用原理了。公式&#xff1a; 响应式对象 代理 触发器。 但是实际使用结果和预期还是不一致。具体现象是数据修改了&#xff0c;但是并没有实现响应式更新界面。即出现了响应式丢失现象。 一、什么情况下对象的响应式会丢失&#xff1f; 一般网…

【网络】协议与网络版计算器

协议与网络版计算器 文章目录 1.协议的概念 1.1序列化与反序列化 2.网络版计算器 2.1封装套接字2.2协议定制 2.2.1Jsoncpp2.2.2报文处理 2.3会话层&#xff1a;TcpServer2.4应用层&#xff1a;Calculate2.5表示层&#xff1a;Service2.6应用层、表示层和会话层->应用层 …

C# 添加图标

一、前言 为应用程序添加图标是优化用户界面、提升应用辨识度的重要操作。合适的图标能帮助用户快速识别和区分不同应用&#xff0c;增强应用的易用性和专业性。 本指南旨在为你提供详细、易懂的步骤&#xff0c;教你如何为应用程序的窗体添加图标。从图标素材的获取到具体的…

使用新版本golang项目中goyacc依赖问题的处理

背景 最近项目使用中有用到go mod 和 goyacc工具。goyacc涉及到编译原理的词法分析&#xff0c;文法分析等功能&#xff0c;可以用来生成基于golang的语法分析文件。本期是记录一个使用中遇到的依赖相关的问题。因为用到goyacc&#xff0c;需要生成goyacc的可执行文件。 而项目…

WPS的AI助手进化跟踪(灵犀+插件)

Ver V0.0 250216: 如何给WPS安装插件用以支持其他大模型LLM V0.1 250217: WPS的灵犀AI现在是DeepSeek R1(可能是全参数671B) 前言 WPS也有内置的AI&#xff0c;叫灵犀&#xff0c;之前应是自已的LLM模型&#xff0c;只能说是属于“能用&#xff0c;有好过无”&#xff0c;所…

计算机视觉:卷积神经网络(CNN)基本概念(一)

第一章&#xff1a;计算机视觉中图像的基础认知 第二章&#xff1a;计算机视觉&#xff1a;卷积神经网络(CNN)基本概念(一) 第三章&#xff1a;计算机视觉&#xff1a;卷积神经网络(CNN)基本概念(二) 第四章&#xff1a;搭建一个经典的LeNet5神经网络 一、引言 卷积神经网络&…