【每日算法】Day 17-1:位图(Bitmap)——十亿级数据去重与快速检索的终极方案(C++实现)
解锁海量数据处理的极致空间效率!今日深入解析位图的核心原理与实战应用,从基础操作到分块优化,彻底掌握仅用1bit存储一个数据的压缩艺术。
一、位图核心思想
位图(Bitmap) 是一种通过比特位表示数据存在性的数据结构,核心特性:
空间压缩:每个元素仅占用1bit空间
精确存储:无哈希冲突,无概率型误判
高效操作:位运算实现O(1)复杂度查询与更新
适用场景:
-
海量整数去重(如十亿级数据去重仅需约120MB内存)
-
快速存在性检查
-
数据排序与范围查询
与布隆过滤器的对比:
| 特性 | 位图 | 布隆过滤器 |
|---|---|---|
| 存储方式 | 精确存储 | 概率型存储 |
| 空间占用 | 依赖数据范围 | 依赖数据量和误判率 |
| 误判率 | 无 | 可调节 |
| 支持操作 | 存在性检查/排序/去重 | 仅存在性检查 |
二、位图实现原理
1. 存储结构
-
比特数组:用基本类型数组(如
uint32_t[])模拟连续比特位 -
索引计算:
-
数组下标:
index = num / 32 -
位偏移:
offset = num % 32
-
2. 核心操作
| 操作 | 公式 | 示例(num=35) | ||
|---|---|---|---|---|
| 设置位 | `bits[index] | = (1 << offset)` | `bits[1] | = (1 << 3)` |
| 清除位 | bits[index] &= ~(1 << offset) | bits[1] &= ~(1 << 3) | ||
| 检查位 | bits[index] & (1 << offset) | bits[1] & (1 << 3) != 0 |
三、C++手写位图
class Bitmap {
private:static const int BIT_PER_WORD = 32;vector<uint32_t> bits;// 计算索引位置inline pair<int, uint32_t> getPos(int num) const {return {num / BIT_PER_WORD, 1U << (num % BIT_PER_WORD)};}public:Bitmap(int maxNum) {int size = (maxNum + BIT_PER_WORD - 1) / BIT_PER_WORD;bits.resize(size, 0);}void add(int num) {auto [index, mask] = getPos(num);bits[index] |= mask;}void remove(int num) {auto [index, mask] = getPos(num);bits[index] &= ~mask;}bool contains(int num) const {auto [index, mask] = getPos(num);return (bits[index] & mask) != 0;}// 返回所有存储的数字(用于去重后导出)vector<int> getAll() {vector<int> res;for (int i = 0; i < bits.size(); ++i) {for (int j = 0; j < BIT_PER_WORD; ++j) {if (bits[i] & (1U << j)) {res.push_back(i * BIT_PER_WORD + j);}}}return res;}
};
四、五大应用场景
场景1:十亿级手机号去重
需求:
11位手机号去重(范围0-99,999,999,999)
位图优化:
-
使用分块位图,按前3位分块(1000块)
-
每块处理8位数字(约需125MB/块)
-
总内存:1000×125MB = 125GB → 实际分批处理可降至10GB内
场景2:快速排序(无重复数字)
void bitmapSort(vector<int>& nums) {if (nums.empty()) return;int maxNum = *max_element(nums.begin(), nums.end());Bitmap bm(maxNum);for (int num : nums) bm.add(num);nums.clear();vector<int> sorted = bm.getAll();nums.swap(sorted);
}
场景3:检测重复元素(LeetCode 217)
bool containsDuplicate(vector<int>& nums) {// 假设数据范围已知(此处示例为0-10^5)Bitmap bm(100000);for (int num : nums) {if (bm.contains(num)) return true;bm.add(num);}return false;
}
场景4:找缺失数字(LeetCode 268)
int missingNumber(vector<int>& nums) {Bitmap bm(nums.size());for (int num : nums) {if (num <= nums.size()) bm.add(num);}for (int i = 0; i <= nums.size(); ++i) {if (!bm.contains(i)) return i;}return -1;
}
场景5:字符去重(ASCII字符集)
string removeDuplicate(string s) {Bitmap bm(128); // ASCII范围0-127string res;for (char c : s) {if (!bm.contains(c)) {res += c;bm.add(c);}}return res;
}
五、位图优化技巧
| 优化方法 | 实现原理 | 适用场景 |
|---|---|---|
| 分块位图 | 将大范围分割为多个小位图 | 数据范围过大时 |
| 压缩位图 | 使用RLE/位压缩算法减少内存 | 稀疏数据存储 |
| 并行位图 | 利用SIMD指令加速批量位操作 | 高性能计算场景 |
| 混合存储 | 结合布隆过滤器处理超大范围 | 允许概率型误判时 |
六、大厂真题实战
真题1:十亿级IP地址统计(某大厂2023面试)
需求:
统计独立IP数量,内存限制2GB
位图分块解法:
class IPBitmap {
private:static const int BLOCK_BITS = 24; // 高8位分块(256块)vector<Bitmap> blocks;public:IPBitmap() : blocks(1 << 8, Bitmap(1 << 24)) {}void addIP(uint32_t ip) {int blockID = ip >> 24;int lowPart = ip & 0xFFFFFF;blocks[blockID].add(lowPart);}int countDistinct() {int cnt = 0;for (auto& bm : blocks) {cnt += bm.getAll().size();}return cnt;}
};
真题2:实时在线用户统计(某大厂2024笔试)
需求:
实时统计最近5分钟的活跃用户数(用户ID范围1-1e9)
滑动窗口+位图优化:
class OnlineUsers {
private:deque<pair<Bitmap, time_t>> window;const int WINDOW_SIZE = 300; // 5分钟(秒)public:void heartBeat(int uid) {time_t now = time(nullptr);if (window.empty() || now - window.back().second >= 1) {window.emplace_back(Bitmap(1e9), now);}window.back().first.add(uid);// 移除过期窗口while (!window.empty() && now - window.front().second > WINDOW_SIZE) {window.pop_front();}}int getOnlineCount() {Bitmap merged(1e9);for (auto& [bm, _] : window) {for (int uid : bm.getAll()) {merged.add(uid);}}return merged.getAll().size();}
};
七、常见误区与优化
-
范围估算错误:未预留足够空间导致溢出
-
线程安全问题:多线程操作需加锁或使用线程本地存储
-
位运算错误:位移操作符优先级陷阱(需加括号)
-
内存对齐优化:按CPU缓存行对齐提升访问速度
LeetCode真题训练:
-
217. 存在重复元素
-
268. 丢失的数字
-
442. 数组中重复的数据
相关文章:
【每日算法】Day 17-1:位图(Bitmap)——十亿级数据去重与快速检索的终极方案(C++实现)
解锁海量数据处理的极致空间效率!今日深入解析位图的核心原理与实战应用,从基础操作到分块优化,彻底掌握仅用1bit存储一个数据的压缩艺术。 一、位图核心思想 位图(Bitmap) 是一种通过比特位表示数据存在性的数据结构…...
7-1 素数求和(线性筛实现)
7-1 素数求和。 分数 10 中等 全屏浏览 切换布局 作者 魏英 单位 浙江科技大学 输入两个正整数m和n(1<m<n<500)统计并输出m和n之间的素数个数以及这些素数的和。 输入格式: 输入两个正整数m和n(1<m<n<500࿰…...
NLP简介及其发展历史
自然语言处理(Natural Language Processing,简称NLP)是人工智能和计算机科学领域中的一个重要分支,致力于实现人与计算机之间自然、高效的语言交流。本文将介绍NLP的基本概念以及其发展历史。 一、什么是自然语言处理?…...
ZKmall开源商城多云高可用架构方案:AWS/Azure/阿里云全栈实践
随着企业数字化转型的加速,云计算服务已成为IT战略中的核心部分。ZKmall开源商城作为一款高性能的开源商城系统,其在多云环境下的高可用架构方案备受关注。下面将结合AWS、Azure和阿里云三大主流云平台,探讨ZKmall的多云高可用架构全栈实践。…...
优化 Web 性能:处理非合成动画(Non-Composited Animations)
在 Web 开发中,动画能够增强用户体验,但低效的动画实现可能导致性能问题。Google 的 Lighthouse 工具在性能审计中特别关注“非合成动画”(Non-Composited Animations),指出这些动画可能增加主线程负担,影响…...
Eliet Chat开发日志:信令服务器注册与通信过程
目录 1. 架构设计:信令服务器与客户端 2. 选择技术栈 3. 实现信令服务器 4. 客户端实现 5. 测试 6. 下一步计划 日期:2025年4月5日 今天的工作重点是实现两个设备通过信令服务器注册并请求对方公网地址信息,以便能够进行点对点通信。我…...
leetcode二叉树刷题调试不方便的解决办法
1. 二叉树不易构建 在leetcode中刷题时,如果没有会员就需要将代码拷贝到本地的编译器进行调试。但是leetcode中有一类题可谓是毒瘤,那就是二叉树的题。 要调试二叉树有关的题需要根据测试用例给出的前序遍历,自己构建一个二叉树,…...
颜色性格测试:探索你的内在性格色彩
颜色性格测试:探索你的内在性格色彩 在我们的日常生活中,颜色无处不在,而我们对颜色的偏好往往能反映出我们内在的性格特质。今天我要分享一个有趣的在线工具 —— 颜色性格测试,它能通过你最喜欢的颜色来分析你的性格倾向。 &…...
hashtable遍历的方法有哪些
在 Java 中,遍历 Hashtable(或其现代替代品 HashMap)有多种方式,以下是 6 种常用方法的详细说明和代码示例: 1. 使用 keySet() 增强 for 循环 Hashtable<String, Integer> table new Hashtable<>(); // …...
CMake学习--Window下VSCode 中 CMake C++ 代码调试操作方法
目录 一、背景知识二、使用方法(一)安装扩展(二)创建 CMake 项目(三)编写代码(四)配置 CMakeLists.txt(五)生成构建文件(六)开始调试 …...
浅谈ai - Activation Checkpointing - 时间换空间
前言 曾在游戏世界挥洒创意,也曾在前端和后端的浪潮间穿梭,如今,而立的我仰望AI的璀璨星空,心潮澎湃,步履不停!愿你我皆乘风破浪,逐梦星辰! Activation Checkpointing(激…...
提高MCU的效率方法
要提高MCU(微控制器单元)的编程效率,需要从硬件特性、代码优化、算法选择、资源管理等多方面入手。以下是一些关键策略: 1. 硬件相关优化 时钟与频率: 根据需求选择合适的时钟源(内部/外部振荡器),避免过高的时钟频率导致功耗浪费。关闭未使用的外设时钟(如定时器、UA…...
5G从专家到小白
文章目录 第五代移动通信技术(5G)简介应用场景 数据传输率带宽频段频段 VS 带宽中低频(6 GHz以下):覆盖范围广、穿透力强高频(24 GHz以上):满足在热点区域提升容量的需求毫米波热点区…...
神经网络入门:生动解读机器学习的“神经元”
神经网络作为机器学习中的核心算法之一,其灵感来源于生物神经系统。在本文中,我们将带领大家手把手学习神经网络的基本原理、结构和训练过程,并通过详细的 Python 代码实例让理论与实践紧密结合。无论你是编程新手还是机器学习爱好者…...
web漏洞靶场学习分享
靶场:pikachu靶场 pikachu漏洞靶场漏洞类型: Burt Force(暴力破解漏洞)XSS(跨站脚本漏洞)CSRF(跨站请求伪造)SQL-Inject(SQL注入漏洞)RCE(远程命令/代码执行)Files Inclusion(文件包含漏洞)Unsafe file downloads(不安全的文件下载)Unsafe file uploads(不安全的文…...
vue watch和 watchEffect
在 Vue 3 中,watch 和 watchEffect 是两个用于响应式地监听数据变化并执行副作用的 API。它们在功能上有一些相似之处,但用途和行为有所不同。以下是对 watch 和 watchEffect 的详细对比和解释: 1. watch watch 是一个更通用的 API…...
函数和模式化——python
一、模块和包 将一段代码保存为应该扩展名为.py 的文件,该文件就是模块。Python中的模块分为三种,分别为:内置模块、第三方模块和自定义模块。 内置模块和第三方模块又称为库内置模块,有 python 解释器自带,不用单独安…...
Python解决“数字插入”问题
Python解决“数字插入”问题 问题描述测试样例解题思路代码 问题描述 小U手中有两个数字 a 和 b。第一个数字是一个任意的正整数,而第二个数字是一个非负整数。她的任务是将第二个数字 b 插入到第一个数字 a 的某个位置,以形成一个最大的可能数字。 你…...
Go语言的嵌入式网络
Go语言的嵌入式网络 引言 在当今快速发展的互联网时代,嵌入式系统和网络技术的结合变得越来越普遍。嵌入式系统是指嵌入到设备中以实现特定功能的计算机系统,它们通常具有资源有限的特点。随着物联网(IoT)的兴起,嵌入…...
Dart 语法
1. 级联操作符 … var paint Paint()..color Colors.black..strokeCap StrokeCap.round..strokeWidth 5.0;2. firstWhereOrNull 3. 隐藏或导入部分组件 // Import only foo. import package:lib1/lib1.dart show foo;// Import all names EXCEPT foo. import package:lib…...
MCP over MQTT:EMQX 开启物联网 Agentic 时代
前言 随着 DeepSeek 等大语言模型(LLM)的广泛应用,如何找到合适的场景,并基于这些大模型构建服务于各行各业的智能体成为关键课题。在社区中,支持智能体开发的基础设施和工具层出不穷,其中,Ant…...
ACM代码模式笔记
系列博客目录 文章目录 系列博客目录1.换行符 1.换行符 nextInt()、nextDouble() 等不会消耗换行符: 当使用 nextInt() 或 nextDouble() 读取数字时,它只读取数字部分,不会消耗掉输入后的换行符。 nextLine() 会读取并消耗换行符:…...
相干光信号处理的一些基础知识
1. 逆琼斯矩阵问题 逆琼斯矩阵方法常用于逆向补偿由光学系统或传输信道(如光纤)引入的偏振态(SOP, State of Polarization)畸变。 1.1 琼斯向量 任意偏振光可用二维复数向量表示: E [ E x E y ] [ ∣ E x ∣ e i…...
WiFi加密协议
目录 1. 认证(Authentication) 1.1 开放系统认证(Open System Authentication) 1.2 共享密钥认证(Shared Key Authentication) 1.3 802.1X/EAP认证(企业级认证) 2. 关联(Association) 3. 加密协议(Security Handshake) 整体流程总结…...
程序化广告行业(61/89):DSP系统活动设置深度剖析
程序化广告行业(61/89):DSP系统活动设置深度剖析 大家好!在程序化广告的学习道路上,我们已经探索了不少重要内容。今天依旧本着和大家一起学习进步的想法,深入解析DSP系统中活动设置的相关知识。这部分内容…...
[王阳明代数讲义]具身智能才气等级分评价排位系统领域投射模型讲义
具身智能才气等级分评价排位系统领域投射模型讲义 具身智能胆识曲线调查琴语言的行为主义特性与模式匹配琴语言的"气质邻域 "与气度,云藏山鹰符号约定 琴语言的"气质邻域 "与气度,一尚韬竹符号约定 琴语言的"气质邻域 "与…...
【Block总结】PlainUSR的局部注意力,即插即用|ACCV2024
论文信息 标题: PlainUSR: Chasing Faster ConvNet for Efficient Super-Resolution作者: Yan Wang, Yusen Li, Gang Wang, Xiaoguang Liu发表时间: 2024年会议/期刊: 亚洲计算机视觉会议(ACCV 2024)研究背景: 超分辨率(Super-Resolution, S…...
Kubernetes集群管理详解:从入门到精通
1. 引言 Kubernetes(简称k8s)作为当今最流行的容器编排平台,已成为云原生应用部署和管理的事实标准。本文将深入探讨k8s集群管理的各个方面,为运维工程师和开发人员提供一个全面的指南。 2. Kubernetes架构概览 在深入具体的管理任务之前,让我们先回顾一下Kubernetes的基本架…...
Git 换行符警告(LF replaced by CRLF)的解决方案
根据你的日志和知识库中的信息,以下是针对 Git 换行符警告(LF replaced by CRLF) 的解决方案: 一、问题分析 警告原因 你当前在 Windows 系统 上工作,但某些文件(如 .gitignore, README.md, package.json 等…...
【C++】从零实现Json-Rpc框架(2)
目录 JsonCpp库 1.1- Json数据格式 1.2 - JsonCpp介绍 • 序列化接口 • 反序列化接口 1.3 - Json序列化实践 JsonCpp使用 Muduo库 2.1 - Muduo库是什么 2.2 - Muduo库常见接口介绍 TcpServer类基础介绍 EventLoop类基础介绍 TcpConnection类基础介绍 TcpClient…...
