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

在这里插入图片描述

目录

  • 前言
  • 实现
    • 完整代码
  • 参考资料

前言

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/64return (int) num / BIT_SIZE;}/*** 得到num在64bit数组上的分布* position表示num中整除了64bit后还余下多少bit*/public long getPosition(long num){// num%64return (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/64return (int) num / BIT_SIZE;}/*** 得到num在64bit数组上的分布* position表示num中整除了64bit后还余下多少bit*/public long getPosition(long num){// num%64return (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();// 模拟手机号数据,将已存在的手机号设置为truefor (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

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

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

相关文章

笔记 | 非素数个数(朴素筛查 || 埃式筛查法)

非素数个数 题目描述朴素筛查方法题解 题目描述 求a-b之间的非素数个数 特别的&#xff0c;1也算作素数&#xff0c;区间是[a, b]。 输入输出格式 输入描述: 多组测试数据。 输入两个正整数数a,b&#xff0c;其中a<b<10^7。 输出描述: 输出答案。 输入输出样例 输入样例…

蓝牙资讯|三星Galaxy Wearable有望在国内发售,智能戒指成为新宠儿

博主 i 冰宇宙 此前爆料&#xff0c;明年 1 月 Galaxy S24 发布会上&#xff0c;智能戒指预计将成为明星产品。而在最新的三星 Galaxy Wearable 应用中&#xff0c;已经出现了一个戒指图标&#xff0c;进一步印证了产品的消息。 Galaxy Wearable 是由三星官方出品的一款针对旗…

geetest极验空间推理验证码破解

geetest极验空间推理验证码破解 > 本篇文章仅供学习使用&#xff0c;如侵权请联系删除。某验验证码3代&#xff0c;4代已经是老生常谈的问题了&#xff0c;本篇文章粗略的分析一波3代空间推理验证码识别与验证的整个过程。 1.参数破解 从一开始gt和challenge的参数的获取…

按键点亮led灯

原理图: K0这个按键按下时&#xff0c;开发板D1这个灯亮&#xff0c;松开&#xff0c;灯灭 代码如下: #include "stm32f4xx.h" void LED_Init(void) {//1.定义一个GPIO外设的结构体变量 GPIO_InitTypeDef GPIO_InitStructure;//RCC_AHB1PeriphClockCmd(RCC_AHB1Pe…

算法题笔记 1-5

目录 week 11. 找出数组中重复的数字题目数据范围样例题解(数组遍历) O(n) 2. 不修改数组找出重复的数字题目数据范围样例题解(分治&#xff0c;抽屉原理) O(nlogn) 3. 二维数组中的查找题目题解(单调性扫描) O(nm) 4.替换空格题目题解(线性扫描) O(n)(双指针扫描) O(n) 5.从尾…

uniapp开发小程序中实现骨架屏

第一步&#xff1a;小程序中实现骨架屏在微信开发者工具中点击生成骨架屏&#xff1a; 第二步&#xff1a;复制html代码&#xff0c;到骨架屏vue组件汇中再把之前写的样式代码引入进去&#xff1a; import ../../pages/user/user.css; 第三步&#xff1a;组件中引入骨架屏&am…

王江涛十天搞定考研词汇

学习目标&#xff1a; 考研词汇 学习内容&#xff1a; 2023-9-17 第一天考研词汇 学习时间&#xff1a; 2023-9-17 学习产出&#xff1a;A intellect智力&#xff1b;知识分子intellectual智力的&#xff1b;聪明的intellectualize使...理智化&#xff0c;对...做理性探索c…

【深度学习】 Python 和 NumPy 系列教程(五):Python容器:3、集合Set详解(初始化、访问元素、常用操作、常用函数)

目录 一、前言 二、实验环境 三、Python容器&#xff08;Containers&#xff09; 0、容器介绍 1、列表&#xff08;List&#xff09; 2、元组&#xff08;Tuple&#xff09; 3、集合&#xff08;Set&#xff09; 1. 初始化 2. 访问集合元素 3. 常用操作 a. 添加单个…

C# 实现迷宫游戏

智能提示&#xff1a; /// <summary>/// 迷宫/// </summary>internal class Maze : IDisposable{private MazeCell[,] cells;private readonly Stack<MazeCell> stack new Stack<MazeCell>();private readonly Random rand new Random();private int…

83 # 静态服务中间件 koa-static 的使用以及实现

静态服务中间件&#xff1a;koa-static 中间件可以决定是否向下执行&#xff0c;如果自己可以处理&#xff0c;那么直接处理完毕结束&#xff0c;如果自己处理不了&#xff0c;next 方法会继续向下执行 新建 public 文件夹&#xff0c;里面添加 index.html、style.css 文件 …

安卓判断是否是模拟器,适配主流雷电,MUMU,夜神,逍遥

前言 最近游戏项目组又有新的要求&#xff0c;对于数据上报和数据统计接口&#xff0c;尽可能的具体化&#xff0c;比如是否是模拟器&#xff0c;模拟器的型号&#xff0c;品牌等&#xff0c;都要求统计&#xff0c;后续模拟器玩家在活动发放&#xff0c;安全风控等方面也易于…

自研一个简易版本的OkHTTP

一,背景 为了彻底搞明白okhttp原理&#xff0c;仿照okhttp自研一个 二&#xff0c;思路 业务上没发出一个request&#xff0c;使用AsyncCall包装起来&#xff0c;然后在网络分发器的作用下&#xff0c;执行具体的每一个Call,这些具体的Call会经过层层的拦截器&#xff0c;最终…