高效查找秘密武器一:位图
有这样的一个问题:
给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数 中。
那么我们一般会想到这样做的
1.遍历,时间复杂度O(n)
2.排序(N*logN),然后二分查找
那么此时此刻,我们再次细想一下,这些方法一般放在内存中进行的,那么40亿个不重复的无符号整数,有多大容量呢?
首先,10亿个字节大概是1个G
那么10亿个整数大约是4个G
然后呢40亿个整数大约是16个G
所以,如若这么大容量放到电脑内存上跑,恰好此时电脑内存也是16个G,那么不是刚刚好?
其实不然,电脑上不单单只是跑这个程序,后面还有很多,比如微信啊,爱奇艺什么的,所以一般来说,这么大容量放到16G的电脑内存是不太现实的。
那么还有什么解决办法呢?
那就请出今天的主角之一:位图
位图
那么什么又是位图呢?
这里说的位图指的是数据结构当中的,不是那个指图像那个位图哦!
位图:所谓的位图,就是用每一位来存放某种状态的。
适用于海量数据,整数,数据无重复的场景。通常用来判定某个数据存不存在的。
那么举个例子来看看
我这里,假设有了一个字节数组,然后有三个字节,那么一个字节呢,又对应8个比特位
一个比特位呢,又可以代表0/1两种状态。
所以数组arr中,每个数字可以通过某个算式来确定某个位置,然后将某个位置的比特位设置为1,说明,此数字存在。
比如,就11来说
11/8=1
那么我们就放到这个1下标
11%8=3
那么我们就在数组1下标下的第三个比特位设置为1,
然后可以说明数字是存在过的
这里值得注意下,为什么比特位标明的数字从右到左呢?
这里是为了方便,自己也是可以左到右的。
那么回过头细想一下,一个字节有8个比特位,且8个比特位都可以表示状态。
那么10亿个字节大概是0.9G,接近1G
10亿个比特位大概是119兆,看作128兆
那么40亿个比特位,那么就是可以看作512兆。
所以内存量一下子大大缩小了。
那么你看这个位图是不是很强大呢!
ok,那么接下来讲讲如何实现它吧。
由上述刚刚的例子可以看得出,底层还是一个数组来的,而且还是一个字节数组。
然后呢,我们创建它的时候,默认给定一个容量。
但是,有时候,需要用户指定它需要范围是多大,那么可以这样写。
//位图底层还是一个数组public byte [] elem;//记录存放数据多少public int Usize;public MyBitMap(){//默认给定的数组大小是1个字节this.elem=new byte[1];}public MyBitMap(int n){this.elem=new byte[n/8+1];}
在给定参数的构造方法中,假设我们给定数字范围是10个,那么10/8=1,但是还余下两个,9 10
所以我们直接给多一个字节是可以的。set
那么接着讲讲set吧
set:
思路:
当然这是核心思路,然后代码实现方面还是需要考虑一些细节
比如,我们set方法,参数传入一个数字的话,如若数字给到的是<0的数字,那么此时是不符合我们位图的
再者如果是给定的数字,如若在找数组下标时超出数组范围,这时候也是需要处理的,比如扩容操作。
那么代码如下:
public void set(int val){if(val<0){//需要抛出异常,因为会导致数组越界throw new IndexOutOfBoundsException();}//放到字节数组哪个位置int arrIndex=val/8;if(arrIndex>elem.length){//超出数组范围,进行扩容elem= Arrays.copyOf(elem,arrIndex+1);}//放到字节数组位置的哪个比特位int bitIndex=val%8;elem[arrIndex] |=(1<<bitIndex);Usize++;}
这个找哪个比特位进行修改,其实最开始时讲过了
即val/8=字节数组哪个下标,
val%8=下标内哪个比特位。
所以这里我们就可以找到,哪个字节哪个比特位要进行修改状态,比如举的例子,设置5这个数字
5/8=0,即在数组0下标
5%8=5,即在比特位第六个位置
get:
这个get方法是返回这个数字是否是存在过的。
所以呢,我们还得是找到这个数字在哪先,然后判断下这个数字的比特位是否是1
举例:
代码:
public boolean get(int val){if(val<0){throw new IndexOutOfBoundsException();}int arrIndex=val/8;int bitIndex=val%8;if((elem[arrIndex]&(1<<bitIndex))!=0){return true;}return false;}
reset:
reset方法呢,是把传入参数,对应的比特重新置为0
举例说明:
代码:
public void reset(int val){if(val<0){throw new IndexOutOfBoundsException();}int arrIndex=val/8;int bitIndex=val%8;elem[arrIndex] &= ~(1<<bitIndex);Usize--;}public int getSize(){return Usize;}
值得注意的是,进行set的时候,Usize++了
所以写一个方法方法来返回当前设置的个数。
那么位图一些基本方法写完了,这里还差最后一个方法:排序
排序
其实我们位图是可以排序的
拿这里来说:
只要我们从右边开始,遍历当前比特位是1的就可以输出当前的值
那么判断当前位是不是为1,是get方法里的思路是一致的,用上&
那么如何输出当前的数字呢?
比如我要输出的是5,那么此时我们可以这样val=i*8+j
当前的i=0,因为我们输出的数字是5,在字节数组的0下标,当j从下标开始遍历,j=5时,就可以输出这个数字5了
代码:
public static void main(String[] args) {int [] arr=new int[]{3,10,9,5,7,24,18};MyBitMap myBitMap=new MyBitMap(24);//先把数据放进字节数组中for(int i=0;i<arr.length;i++){myBitMap.set(arr[i]);}//排序for(int i=0;i< myBitMap.elem.length;i++){for(int j=0;j<8;j++){if((myBitMap.elem[i]&(1<<j))!=0){System.out.print(i*8+j+" ");}}}}
ok,那么位图这里,小编分享的差不多了。
顺带提一句,一开始给的那道题是腾讯曾经的面试题来的。
源码:
public class MyBitMap {//位图底层还是一个数组public byte [] elem;//记录存放数据多少public int Usize;public MyBitMap(){//默认给定的数组大小是1个字节this.elem=new byte[1];}public MyBitMap(int n){this.elem=new byte[n/8+1];}/*** set操作,对添加进来的数据置为1,即为存在的意思* @param val*/public void set(int val){if(val<0){//需要抛出异常,因为会导致数组越界throw new IndexOutOfBoundsException();}//放到字节数组哪个位置int arrIndex=val/8;if(arrIndex>elem.length){//超出数组范围,进行扩容elem= Arrays.copyOf(elem,arrIndex+1);}//放到字节数组位置的哪个比特位int bitIndex=val%8;elem[arrIndex] |=(1<<bitIndex);Usize++;}/*** get方法返回比特位是否设置过1* @param val* @return*/public boolean get(int val){if(val<0){throw new IndexOutOfBoundsException();}int arrIndex=val/8;int bitIndex=val%8;if((elem[arrIndex]&(1<<bitIndex))!=0){return true;}return false;}public void reset(int val){if(val<0){throw new IndexOutOfBoundsException();}int arrIndex=val/8;int bitIndex=val%8;elem[arrIndex] &= ~(1<<bitIndex);Usize--;}public int getSize(){return Usize;}public static void main(String[] args) {int [] arr=new int[]{3,10,9,5,7,24,18};MyBitMap myBitMap=new MyBitMap(24);//先把数据放进字节数组中for(int i=0;i<arr.length;i++){myBitMap.set(arr[i]);}//排序for(int i=0;i< myBitMap.elem.length;i++){for(int j=0;j<8;j++){if((myBitMap.elem[i]&(1<<j))!=0){System.out.print(i*8+j+" ");}}}}
}
相关文章:

高效查找秘密武器一:位图
有这样的一个问题: 给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数 中。 那么我们一般会想到这样做的 1.遍历,时间复杂度O(n) 2.排序(N*logN),…...

自回归模型(AR )
最近看到一些模型使用了自回归方法,这里就学习一下整理一下相关内容方便以后查阅。 自回归模型(AR ) 自回归模型(AR )AR 模型的引入AR 模型的定义参数的估计方法模型阶数选择平稳性与因果性条件自相关与偏自相关函数优…...
Linux内核 -- Linux驱动从设备树dts文件中读取字符串信息的方法
从Linux设备树读取字符串信息 在Linux内核中,从设备树(DTS)中读取字符串信息,通常使用内核提供的设备树解析API。这些API主要位于<linux/of.h>头文件中。 常用函数解析 1. of_get_property 获取设备树中的属性。原型:con…...
图片懒加载+IntersectionObserver
通过IntersectionObserver实现图片懒加载 在JavaScript中,图片懒加载可以通过监听滚动事件和计算图片距离视口顶部的距离来实现 在HTML中,将src属性设置为一个透明的1x1像素图片作为占位符,并将实际的图片URL设置为data-src属性。 <img c…...

MySQL的获取、安装、配置及使用教程
一、获取MySQL 官网地址:https://www.mysql.com MySQL产品:企业版(Enterprise)和社区版(Community)社区版是通过GPL协议授权的开源软件,可以免费使用。企业版是需要收费的商业软件 MySQL版本历史:5.0、5.5、5.6、5.7和8.0(最新版本)两种打包版本:MSI(安装版)和ZI…...

Odoo在线python代码开发
《Odoo在线python代码开发从入门到精通》 从简入手,由浅入深,Odoo开发不求人 以实例促理解,举一反三 从Python到Odoo,低代码开发的正解之路 代码视频讲解与代码注释配合,帮助用户真正理解每一句代码的作用 《Odoo在…...

在.NET 6中使用Serilog收集日志
此示例的完整详细信息:https://download.csdn.net/download/hefeng_aspnet/89998498 Serilog 是一个日志库,它提供对文件、控制台和其他几个地方的记录。它易于配置,并且具有干净且易于使用的界面。 Serilog具有无与伦比的输出目的地选择&…...

【D3.js in Action 3 精译_043】5.1 饼图和环形图的创建(中):D3 饼图布局生成器的配置方法
当前内容所在位置: 第五章 饼图布局与堆叠布局 ✔️ 5.1 饼图和环形图的创建 ✔️ 5.1.1 准备阶段(上篇)5.1.2 饼图布局生成器(中篇) ✔️5.1.3 圆弧的绘制5.1.4 数据标签的添加 文章目录 5.1.2 饼图布局生成器 The …...

离线安装ollama到服务器
搜了很多教程不满意,弄了半天才弄好,这里记录下,方便以后的人用,那个在线下载太慢,怕不是得下载到明年。 一.从官网下在liunx版的tgz安装包 Releases ollama/ollama (github.com) 查看自己的服务器信息(参考 https:/…...
自动化点亮LED灯之程序编写
程序编写: #!/bin/shecho none > /sys/class/leds/led1/triggerecho none > /sys/class/leds/led2/triggerecho none > /sys/class/leds/led3/triggerecho 0 > /sys/class/leds/led1/brightnessecho 0 > /sys/class/leds/led2/brightnessecho 0 >…...

linux 系列服务器 高并发下ulimit优化文档
系统输入 ulimit -a 结果如下 解除或提高 Linux 系统的最大进程数 在高并发场景中,合理设置 Linux 系统的最大进程数对于提升服务器性能至关重要。以下是具体步骤: 临时修改 ulimit 设置 可以通过 ulimit 命令临时调整当前会话的最大进程数。 查看当前…...
人工智能入门数学基础:统计推断详解
人工智能入门数学基础:统计推断详解 目录 前言 1. 统计推断的基本概念 1.1 参数估计 1.2 假设检验 2. 统计推断的应用示例 2.1 参数估计示例:样本均值和置信区间 2.2 假设检验示例:t检验 3. 统计推断在人工智能中的应用场景 总结 前言…...
Spark区分应用程序 Application、作业Job、阶段Stage、任务Task
目录 一、Spark核心概念 1、应用程序Application 2、作业Job 3、阶段Stage 4、任务Task 二、示例 一、Spark核心概念 在Apache Spark中,有几个核心概念用于描述应用程序的执行流程和组件,包括应用程序 Application、作业Job、阶段Stage、任务Task…...

【Liunx篇】基础开发工具 - yum
文章目录 🌵一.Liunx下安装软件的方案🐾1.源代码安装🐾2.rpm包安装🐾3.包管理器进行安装 🌵二.软件包管理器-yum🌵三.yum的具体操作🐾1.查看软件包🐾2.安装软件包🐾3.卸载…...

docker学习笔记(五)--docker-compose
文章目录 常用命令docker-compose是什么yml配置指令详解versionservicesimagebuildcommandportsvolumesdepends_on docker-compose.yml文件编写 常用命令 命令说明docker-compose up启动所有docker-compose服务,通常加上-d选项,让其运行在后台docker-co…...

电子商务人工智能指南 4/6 - 内容理解
介绍 81% 的零售业高管表示, AI 至少在其组织中发挥了中等至完全的作用。然而,78% 的受访零售业高管表示,很难跟上不断发展的 AI 格局。 近年来,电子商务团队加快了适应新客户偏好和创造卓越数字购物体验的需求。采用 AI 不再是一…...

Hadoop3集群实战:从零开始的搭建之旅
目录 一、概念 1.1 Hadoop是什么 1.2 历史 1.3 三大发行版本(了解) 1.4 优势 1.5 组成💗 1.6 HDFS架构 1.7 YARN架构 1.8 MapReduce概述 1.9 HDFS\YARN\MapReduce关系 二、环境准备 2.1 准备模版虚拟机 2.2 安装必要软件 2.3 安…...
Kotlin设计模式之桥接模式
桥接模式用于将抽象部分与实现部分分离,使它们可以独立变化。Kotlin中可以通过接口和抽象类来实现桥接模式。以下是桥接模式的实现方法: 一. 基本桥接模式 在这种模式中,定义一个抽象部分和一个实现部分,通过组合将它们连接起来…...

详解组合模式
引言 有一种情况,当一组对象具有“整体—部分”关系时,如果我们处理其中一个对象或对象组合(区别对待),就可能会出现牵一发而动全身的情况,造成代码复杂。这个时候,组合模式就是一种可以用一致的…...
【系统架构设计师论文】云上自动化运维及其应用
随着云计算技术的迅猛发展,企业对云资源的需求日益增长。为了应对这一挑战,云上自动化运维(CloudOps)应运而生,它结合了DevOps理念和技术,通过自动化工具和流程来提高云环境的管理效率和服务质量。本文将探讨云上自动化运维的主要衡量指标,并详细介绍一个实际项目中如何…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...

在 Visual Studio Code 中使用驭码 CodeRider 提升开发效率:以冒泡排序为例
目录 前言1 插件安装与配置1.1 安装驭码 CodeRider1.2 初始配置建议 2 示例代码:冒泡排序3 驭码 CodeRider 功能详解3.1 功能概览3.2 代码解释功能3.3 自动注释生成3.4 逻辑修改功能3.5 单元测试自动生成3.6 代码优化建议 4 驭码的实际应用建议5 常见问题与解决建议…...
Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解
文章目录 一、开启慢查询日志,定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...

解析“道作为序位生成器”的核心原理
解析“道作为序位生成器”的核心原理 以下完整展开道函数的零点调控机制,重点解析"道作为序位生成器"的核心原理与实现框架: 一、道函数的零点调控机制 1. 道作为序位生成器 道在认知坐标系$(x_{\text{物}}, y_{\text{意}}, z_{\text{文}}…...

聚六亚甲基单胍盐酸盐市场深度解析:现状、挑战与机遇
根据 QYResearch 发布的市场报告显示,全球市场规模预计在 2031 年达到 9848 万美元,2025 - 2031 年期间年复合增长率(CAGR)为 3.7%。在竞争格局上,市场集中度较高,2024 年全球前十强厂商占据约 74.0% 的市场…...