【数据结构】【项目】BitMap?40亿电话号码如何快速去重?

2023-11-08

在这里插入图片描述

前言

40亿电话号码如何快速去重?我们往往会想到bitmap

数据结构中的 Bitmap 是一种位图索引非常高效的数据结构,用于存储处理大规模数据的位信息,其中每个位对应于一个元素,如果位为1,则表示该元素存在于集合中,否则表示不存在。如果要表示一个包含 10 个元素的数据集,可以创建一个包含 10 位的位数组。

在这里插入图片描述

Bitmap 支持插入和查找。插入操作将对应位置的位从 0 设置为 1,将元素添加到数据集中。查找操作通过检查相应位置的位来确定元素是否存在于数据集中。如果位为 1,表示元素存在;如果为 0,表示元素不存在。我们把数字遍历一遍计算放到数组后,就已经是顺序存放的了,遍历取到的就是已经排序后的结果。

Bitmap 非常高效,时间复杂度是O(n)。这是因为位操作本身非常快,并且不受数据集大小的限制。并且Bitmap空间占用非常小,可以大大减小内存消耗。

Bitmap数据结构在搜索引擎、数据库、网络协议等领域都有广泛的应用:

布隆过滤器(Bloom Filter):可以用于快速检查一个元素是否属于一个大型数据集的概率数据结构。

数据库索引:Bitmap 索引可以用于加速数据库查询操作。

网络流量分析:Bitmap 可以用于跟踪网络流量中的 IP 地址、用户 ID 等信息。

此外,Bitmap 适用于离散的、小范围的整数数据。对于连续范围或具有大范围整数的数据集,Bitmap 可能会变得非常大,这种情况下,其他数据结构比如哈希表或树可能更合适。

优点:

运算效率高,不需要进行比较和移位。

空间占用少。

缺点:

所有的数据不能重复。即不可对重复的数据进行排序和查找。

只有当数据比较密集时才有优势。

实现

给定一个bit数组:

    private long[] bits;

当我们需要插入一个数 num,需要计算这个 num在整个bit数组中应该在哪个位置:


    /**
     * 得到long[]的index
     * index表示num中包含多少个64bit可以被整除
     */
    public int getIndex(long num){
        // num/64
        return (int) num / BIT_SIZE;
    }

    /**
     * 得到num在64bit数组上的分布
     * position表示num中整除了64bit后还余下多少bit
     */
    public long getPosition(long num){
        // num%64
        return (long) num % BIT_SIZE;
    }

我们需要知道一个num能映射出多少个bit。

首先long类型相当于64bit,num整除64,计算num中有多少个long,得到 index。而后再num%64,取整除64后的余数,得到position。这两者加起来其实就是num的总bit数,他们共同能够索引到num能映射到的bit位。

我们有插入函数如下:

    /**
     * 添加num
     * 根据num包含多少个long确定在bitmap中的index,根据num÷取余确定在bitmap中除不尽的bit占用
     */
    public void add(long num){
        int index = getIndex(num);
        long position = getPosition(num);
        // 将1左移position位后,position那个位置就是1,
        // 然后数组的index位置做与运算,这样index索引中的position位置就替换成1了
        bits[index] = bits[index] | (1 << position);
    }

只要确定的num的bit占用,我们把数组中对应bit位置置为1即可,这个位置就唯一代表我们插入的num。查询也是同理。

 
    /**
     * 判断指定数字num是否存在
     */
    public boolean contains(int num){
        int index = getIndex(num);
        long position = getPosition(num);
        // 将1左移position后,position那个位置就是1,然后和以前的数据做与运算,判断是否为0即可
        return (bits[index] & 1 << position) != 0;
    }

完整代码

我们添加了测试代码,首先电话号码是11位的数字,但是你会发现对于java来说11位数字太长太大了,没办法直接存。我们把11位的电话号码拆分成前后两部分,分成两个bitmap来分别判断,当电话号码的两部分存在重复则认为这个电话号码是重复的。

完整代码如下:

BitMap.java

package org.example.bitmap;

import java.util.BitSet;
import java.util.Random;

/**
 * 位图
 */
public class BitMap {

    private long[] bits;

    private int BIT_SIZE =64;

    public BitMap() {
        bits = new long[93750000];
    }

    public BitMap(int n) {
        bits = new long[n];
    }

 
    /**
     * 添加num
     * 根据num包含多少个long确定在bitmap中的index,根据num÷取余确定在bitmap中除不尽的bit占用
     */
    public void add(long num){
        int index = getIndex(num);
        long position = getPosition(num);
        // 将1左移position位后,position那个位置就是1,
        // 然后数组的index位置做与运算,这样index索引中的position位置就替换成1了
        bits[index] = bits[index] | (1 << position);
    }
 
    /**
     * 判断指定数字num是否存在
     */
    public boolean contains(int num){
        int index = getIndex(num);
        long position = getPosition(num);
        // 将1左移position后,position那个位置就是1,然后和以前的数据做与运算,判断是否为0即可
        return (bits[index] & 1 << position) != 0;
    }

    /**
     * 重置num在位图的索引位置
     */
    public void clear(long num){
        int index = getIndex(num);
        long position = getPosition(num);
        // 对1进行左移position个位置,然后取反,最后与byte[index]进行与操作
        bits[index] &= ~(1 << position);
    }

    /**
     * 得到long[]的index
     * index表示num中包含多少个64bit可以被整除
     */
    public int getIndex(long num){
        // num/64
        return (int) num / BIT_SIZE;
    }

    /**
     * 得到num在64bit数组上的分布
     * position表示num中整除了64bit后还余下多少bit
     */
    public long getPosition(long num){
        // num%64
        return (long) num % BIT_SIZE;
    }

    public int cardinality() {
        int sum = 0;
        for (int i = 0; i < bits.length; i++) {
            sum += Long.bitCount(bits[i]);
        }
        return sum;
    }

    public static void main(String[] args) {
        // 假设有40亿手机号
        long numberOfPhoneNumbers = 4_000_000_000L;
        // 存储前3位,最多100个可能的组合
        BitMap firstDigitsSet = new BitMap(100);
        // 存储后8位,最多 100_000_000 个可能的组合
        BitMap lastDigitsSet = new BitMap(100_000_000);

        long time = System.currentTimeMillis();

        // 模拟手机号数据,将已存在的手机号设置为true
        for (long i = 0; i < numberOfPhoneNumbers; i++) {
            // 手机号
            String phoneNumber = generateRandomPhoneNumber();

            // 提取手机号的前3位和后8位
            String firstDigits = phoneNumber.substring(0, 3);
            String lastDigits = phoneNumber.substring(3);
            int firstDigitsIndex = Integer.parseInt(firstDigits);
            int lastDigitsIndex = Integer.parseInt(lastDigits);

            // 检查前2位是否重复
            if (firstDigitsSet.contains(firstDigitsIndex)) {
                // 检查后9位是否重复
                if (lastDigitsSet.contains(lastDigitsIndex)) {
                    // 打印重复的号码
                    StringBuilder sb = new StringBuilder(firstDigits);sb.append(lastDigits);
                    System.out.println("重复的号码:" + sb.toString());
                } else {
                    // 不重复则存储号码后9位
                    lastDigitsSet.add(lastDigitsIndex);
                }

            } else {
                firstDigitsSet.add(firstDigitsIndex);
            }

        }

        time = System.currentTimeMillis() - time;
        System.out.println(time);
    }


    /**
     * 随机电话号码生成
     */
    private static String generateRandomPhoneNumber() {
        Random random = new Random();

        // 随机生成国家和地区代码(假设国家代码为+1,地区代码为区号)
        String countryCode = "+1";
        // 随机生成三位区号
        String areaCode = String.format("%03d", random.nextInt(1000));
        // 随机生成手机号码三位前缀、四位后缀
        String prefix = String.format("%03d", random.nextInt(1000));
        String suffix = String.format("%04d", random.nextInt(10000));

        // 拼接生成的手机号码
        String phoneNumber = countryCode + areaCode + prefix + suffix;

        return phoneNumber;
    }

}


我们直接for循环把40亿个电话生成放入bitmap中,如果bitmap中已存在就打印,不存在则插入。

运行结果如下:

在这里插入图片描述

40亿太多了没等运行完,执行过程中控制台一直在打印,其实速度非常快

参考资料

https://blog.csdn.net/caox_nazi/article/details/95340537

https://blog.csdn.net/jj89929665/article/details/123539866

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【数据结构】【项目】BitMap?40亿电话号码如何快速去重? 的相关文章

随机推荐

  • 华为OD机试 - 阿里巴巴找黄金宝箱(I)(Java)

    题目描述 一贫如洗的樵夫阿里巴巴在去砍柴的路上 无意中发现了强盗集团的藏宝地 藏宝地有编号从0 N的箱子 每个箱子上面贴有一个数字 箱子中可能有一个黄金宝箱 黄金宝箱满足排在它之前的所有箱子数字和等于排在它之后的所有箱子数字之和 第一个箱子
  • Verilog实现呼吸灯效果

    呼吸灯的效果采用PWM调波的形式 即快速的改变每个周期的占空比 一个周期内高电平时间占一个周期时间的比值 来实现点亮到熄灭的效果 示意如下图 而关于整个波形图 用50MHz的晶振 从0开始计数到49则为1us 而1ms是1us的1000倍
  • elasticsearch update 无结果

    这个一直不能用 for id in data front res es update index index doc type doc type id id body doc flag 0 print id res break total
  • Android 块设备驱动之二

    将块设备添到系统 添加磁盘和分区到系统中 add diskgendisk函数 register disk add partition 块设备操作 读写操作 请求操作 提交读写请求 将块设备添到系统 调用void blk register r
  • 【Linux】网络编程二:socket简介、字节序、socket地址及地址转换API

    参考连接 https www nowcoder com study live 504 2 16 Linux 网络编程一 网络结构模式 MAC IP 端口 网络模型 协议及网络通信过程简单介绍 Linux 网络编程二 socket简介 字节序
  • JS 终止执行的方法

    一 在function里面 1 return 2 return false 二 非function方法里面 alert before error throw SyntaxError alert after error 三 非function
  • windows下运行pointnet(全)

    放假闲着在家没事 本人突然想跑一下3d深度学习的开山之作 pointnet玩一玩 可是目前网上大部分pointnet的运行教程都是在Ubuntu系统下的 其实本人也曾装过双系统 但是因为我太菜了 在Ubuntu下装完显卡驱动和cuda后切换
  • Linux配置连接wifi功能步骤总结

    1 配置wpa supplicant conf文件 基本内容如下 ctrl interface var run wpa supplicant ctrl interface group 0 update config 1 network ss
  • Visual Studio中输入英文会在字母之间自动增加空格

    现象 不小心按了什么键之后字母之间增加了空格 如下面 在这里插入图片描述 https img blog csdnimg cn b211b973b9c8470fae4402161ddb3935 png 解决办法 针对上面图片中显示的这种英文字
  • 初学Three.js : 场景搭建

    关于Three js 场景搭建的知识 可结合这两篇文章学习 https juejin im post 5ab07d186fb9a028b92cf79d 官方文档 说明 第一篇中给出的three js 我在使用时出现错误 遂引用了官方文档给出
  • win+R键常见命令

    快速启动快捷键 Win R 这个快捷键是 Windows 的一个原生的功能 从 XP 到 Windows 10 都自带了 在使用这个快捷键后 可以打开系统搜索 是一种比较快捷的指令输入方式 系统会弹出一个小窗口让你输入命令 回车后会立即执行
  • 开源音乐播放器!

    导读 音乐是生活的一部分 维基百科关于音乐发展历史的文章有这样一段不错的描述说 全世界所有的人们 包括哪怕是最孤立 与世隔绝的部落 都会有自己的特色音乐 好吧 我们开源人就构成了一个部落 我建议我们的 音乐形式 应该包括开源音乐播放器 在过
  • Spring下集成 3.X 的mongo

    之前的项目中 打算用springmvc 搞个web来方便访问 数据库 当然是用mongo 遇到的问题是 spring下自带的 只支持2 X的 mongo driver 这点 从 只能 get出 DB DBCollection 就可以看出了
  • go使用json

    JavaScript对象表示法 JSON 是一种用于发送和接收结构化信息的标准协议 在类似的协议中 JSON并不是唯一的一个标准协议 XML 7 14 ASN 1和Google的Protocol Buffers都是类似的协议 并且有各自的特
  • 抖音服务器带宽有多大,才能供上亿人同时刷?

    最近看到一个有意思的提问 抖音服务器带宽有多大 为什么能够供那么多人同时刷 今天来给大家科普一下 首先 我们需要了解什么是服务器带宽 服务器带宽指的是数据中心或服务器中心连接到Internet的传输速率 通常用Mbps或Gbps衡量 这决定
  • vue[vue-quill-editor常规使用及样式相关注意事项]

    vue quill editor是当前vue处理富文本相关的使用比较多的一款插件 然而在使用的过程中这款插件还是有不少需要注意的地方 基础使用 npm install vue quill editor save 编写组件 VueQuillE
  • 正则解析SQL表名和SQL类型

    该程序可以对SQL进行解析 对 hint注释 SQL类型 表名 SQL进行解析 import re def extract sql info sql query 正则表达式用于匹配操作类型和表名 i s select insert upda
  • 学习太极创客 — MQTT(六)ESP8266 发布 MQTT 消息

    视频链接 https www bilibili com video BV1Xy4y1z7Mm spm id from autoNext vd source b91967c499b23106586d7aa35af46413 资料链接 http
  • 过来看~/(≧▽≦)/~啦啦啦!!各种书本课后答案!——第二部分:【化学物理】

    各位 注意了 这里的资料非常齐全 希望大家看了之后支持我 谢谢 啦啦啦 http www 3che com fromuid 21434 第二部分 化学物理 http www 3che com forum 26 1 html http www
  • 【数据结构】【项目】BitMap?40亿电话号码如何快速去重?

    目录 前言 实现 完整代码 参考资料 前言 40亿电话号码如何快速去重 我们往往会想到bitmap 数据结构中的 Bitmap 是一种位图索引非常高效的数据结构 用于存储处理大规模数据的位信息 其中每个位对应于一个元素 如果位为1 则表示该