【每日算法】Day 17-1:位图(Bitmap)——十亿级数据去重与快速检索的终极方案(C++实现)

news2025/4/6 15:10:14

解锁海量数据处理的极致空间效率!今日深入解析位图的核心原理与实战应用,从基础操作到分块优化,彻底掌握仅用1bit存储一个数据的压缩艺术。

一、位图核心思想

位图(Bitmap) 是一种通过比特位表示数据存在性的数据结构,核心特性:

  1. 空间压缩:每个元素仅占用1bit空间

  2. 精确存储:无哈希冲突,无概率型误判

  3. 高效操作:位运算实现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-127
    string 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();
    }
};

七、常见误区与优化

  1. 范围估算错误:未预留足够空间导致溢出

  2. 线程安全问题:多线程操作需加锁或使用线程本地存储

  3. 位运算错误:位移操作符优先级陷阱(需加括号)

  4. 内存对齐优化:按CPU缓存行对齐提升访问速度


LeetCode真题训练:

  • 217. 存在重复元素

  • 268. 丢失的数字

  • 442. 数组中重复的数据

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

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

相关文章

7-1 素数求和(线性筛实现)

7-1 素数求和。 分数 10 中等 全屏浏览 切换布局 作者 魏英 单位 浙江科技大学 输入两个正整数m和n&#xff08;1<m<n<500&#xff09;统计并输出m和n之间的素数个数以及这些素数的和。 输入格式: 输入两个正整数m和n&#xff08;1<m<n<500&#xff0…

ZKmall开源商城多云高可用架构方案:AWS/Azure/阿里云全栈实践

随着企业数字化转型的加速&#xff0c;云计算服务已成为IT战略中的核心部分。ZKmall开源商城作为一款高性能的开源商城系统&#xff0c;其在多云环境下的高可用架构方案备受关注。下面将结合AWS、Azure和阿里云三大主流云平台&#xff0c;探讨ZKmall的多云高可用架构全栈实践。…

leetcode二叉树刷题调试不方便的解决办法

1. 二叉树不易构建 在leetcode中刷题时&#xff0c;如果没有会员就需要将代码拷贝到本地的编译器进行调试。但是leetcode中有一类题可谓是毒瘤&#xff0c;那就是二叉树的题。 要调试二叉树有关的题需要根据测试用例给出的前序遍历&#xff0c;自己构建一个二叉树&#xff0c;…

颜色性格测试:探索你的内在性格色彩

颜色性格测试&#xff1a;探索你的内在性格色彩 在我们的日常生活中&#xff0c;颜色无处不在&#xff0c;而我们对颜色的偏好往往能反映出我们内在的性格特质。今天我要分享一个有趣的在线工具 —— 颜色性格测试&#xff0c;它能通过你最喜欢的颜色来分析你的性格倾向。 &…

CMake学习--Window下VSCode 中 CMake C++ 代码调试操作方法

目录 一、背景知识二、使用方法&#xff08;一&#xff09;安装扩展&#xff08;二&#xff09;创建 CMake 项目&#xff08;三&#xff09;编写代码&#xff08;四&#xff09;配置 CMakeLists.txt&#xff08;五&#xff09;生成构建文件&#xff08;六&#xff09;开始调试 …

神经网络入门:生动解读机器学习的“神经元”

神经网络作为机器学习中的核心算法之一&#xff0c;其灵感来源于生物神经系统。在本文中&#xff0c;我们将带领大家手把手学习神经网络的基本原理、结构和训练过程&#xff0c;并通过详细的 Python 代码实例让理论与实践紧密结合。无论你是编程新手还是机器学习爱好者&#xf…

web漏洞靶场学习分享

靶场&#xff1a;pikachu靶场 pikachu漏洞靶场漏洞类型: Burt Force(暴力破解漏洞)XSS(跨站脚本漏洞)CSRF(跨站请求伪造)SQL-Inject(SQL注入漏洞)RCE(远程命令/代码执行)Files Inclusion(文件包含漏洞)Unsafe file downloads(不安全的文件下载)Unsafe file uploads(不安全的文…

MCP over MQTT:EMQX 开启物联网 Agentic 时代

前言 随着 DeepSeek 等大语言模型&#xff08;LLM&#xff09;的广泛应用&#xff0c;如何找到合适的场景&#xff0c;并基于这些大模型构建服务于各行各业的智能体成为关键课题。在社区中&#xff0c;支持智能体开发的基础设施和工具层出不穷&#xff0c;其中&#xff0c;Ant…

ACM代码模式笔记

系列博客目录 文章目录 系列博客目录1.换行符 1.换行符 nextInt()、nextDouble() 等不会消耗换行符&#xff1a; 当使用 nextInt() 或 nextDouble() 读取数字时&#xff0c;它只读取数字部分&#xff0c;不会消耗掉输入后的换行符。 nextLine() 会读取并消耗换行符&#xff1a…

[王阳明代数讲义]具身智能才气等级分评价排位系统领域投射模型讲义

具身智能才气等级分评价排位系统领域投射模型讲义 具身智能胆识曲线调查琴语言的行为主义特性与模式匹配琴语言的"气质邻域 "与气度&#xff0c;云藏山鹰符号约定 琴语言的"气质邻域 "与气度&#xff0c;一尚韬竹符号约定 琴语言的"气质邻域 "与…

【Block总结】PlainUSR的局部注意力,即插即用|ACCV2024

论文信息 标题: PlainUSR: Chasing Faster ConvNet for Efficient Super-Resolution作者: Yan Wang, Yusen Li, Gang Wang, Xiaoguang Liu发表时间: 2024年会议/期刊: 亚洲计算机视觉会议&#xff08;ACCV 2024&#xff09;研究背景: 超分辨率&#xff08;Super-Resolution, S…

【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…

FastAPI依赖注入:链式调用与多级参数传递

title: FastAPI依赖注入:链式调用与多级参数传递 date: 2025/04/05 18:43:12 updated: 2025/04/05 18:43:12 author: cmdragon excerpt: FastAPI的依赖注入系统通过链式调用和多级参数传递实现组件间的解耦和复用。核心特性包括解耦性、可复用性、可测试性和声明式依赖解析…

【STM32单片机】#5 定时中断

主要参考学习资料&#xff1a; B站江协科技 STM32入门教程-2023版 细致讲解 中文字幕 开发资料下载链接&#xff1a;https://pan.baidu.com/s/1h_UjuQKDX9IpP-U1Effbsw?pwddspb 单片机套装&#xff1a;STM32F103C8T6开发板单片机C6T6核心板 实验板最小系统板套件科协 实验&…

OrbStack 作为 Mac 用户的 Docker 替代方案

推荐使用 OrbStack 作为 Mac 用户的 Docker 替代方案 在现代开发环境中,容器化技术已经成为了软件开发的重要组成部分。对于 Mac 用户来说,Docker Desktop 是一个广泛使用的工具,但它并不是唯一的选择。本文将推荐 OrbStack 作为 Docker Desktop 的替代方案,并探讨其优势。…

运行小程序报错

[ app.json 文件内容错误] app.json: ["tabBar"]["list"] 不能超过 5 项(env: Windows,mp,1.06.2206090; lib: 3.7.12) 他的意思大概是&#xff0c;微信小程序 app.json 文件中的 tabBar.list 配置项超过了 5 项。这是微信小程序的限制&#xff0c;tabBar…

深入剖析丝杆升降机工作原理,解锁工业传动奥秘

丝杆升降机&#xff0c;在工业设备的大舞台上扮演着不可或缺的角色&#xff0c;被广泛应用于机械制造、自动化生产线、建筑施工等众多领域。它能够精准实现重物的升降、定位等操作&#xff0c;为各类工业生产提供了稳定可靠的支持。想要深入了解丝杆升降机&#xff0c;就必须探…

【51单片机】2-3【I/O口】震动传感器控制LED灯

1.硬件 51最小系统LED灯模块震动传感器模块 2.软件 #include "reg52.h"sbit led1 P3^7;//根据原理图&#xff08;电路图&#xff09;&#xff0c;设备变量led1指向P3组IO口的第7口 sbit vibrate P3^3;//震动传感器DO接P3.3口void Delay2000ms() //11.0592MHz {…

医疗思维图与数智云融合:从私有云到思维图的AI架构迭代(代码版)

医疗思维图作为AI架构演进的重要方向,其发展路径从传统云计算向融合时空智能、大模型及生态开放的“思维图”架构迭代,体现了技术与场景深度融合的趋势。 以下是其架构迭代的核心路径与关键特征分析: 一、从“智慧云”到“思维图”的架构演进逻辑 以下是针对医疗信息化领域…

【JS】接雨水题解

题目 思路 首先我们要明确如何计算每条柱子的接水量&#xff1a; 每条柱子对应接到的雨水量该柱子左边最大值和右边最大值中的较小值-该柱子本身的高度。举例&#xff1a;第二条柱子自身高度为0&#xff0c;左边最大值为1&#xff0c;右边最大值为3&#xff0c;取较小值1-自身…