位图——哈希思想的应用

三、位图

0、位图概念

所谓位图,就是用每一个比特位来存放某种状态(0或1),是一种哈希思想的应用,适用于海量数据,整数,数据无重复的场景。通常是用来判断某个数据存不存在的。(注意比特位访问的顺序,下图是使用byte[]数组来充当位图,它的第一位是第一个byte的第一个比特位也就是byte[0]最右边的比特位
image.png

2、位图求交、并、差集

image.png

3、位图模拟实现

import java.util.Arrays;

public class MyBitSet {

    public byte[] elem;
    //usedSize记录当前位图当中 存在了多少个有效的数据
    public int usedSize;

    public MyBitSet() {
        this.elem = new byte[1];
    }

    /**
     * @param n 多少位
     *          n = 12
     * 有可能会多给一个字节,但是无所谓。
     */
    public MyBitSet(int n) {
        this.elem = new byte[n/8+1];
    }

    /**
     * 设置某一位为 1
     * @param val
     */
    public void set(int val) {
        if(val < 0) {
            throw new IndexOutOfBoundsException();
        }
        int arrayIndex = val / 8 ;
        //扩容
        if(arrayIndex > elem.length-1) {
            elem = Arrays.copyOf(elem,arrayIndex+1);
        }
        int bitIndex = val % 8;
        // 将elem[arrayIndex]的第bitIndex个比特位设置位1。
        elem[arrayIndex] |=  (1 << bitIndex); 
        usedSize++;
    }

    /**
     * 判断当前位  是不是1
     * @param val
     */
    public boolean get(int val) {
        if(val < 0) {
            throw new IndexOutOfBoundsException();
        }
        int arrayIndex = val / 8 ;
        int bitIndex = val % 8;

        if((elem[arrayIndex] & (1 << bitIndex)) != 0) {
            return true;
        }
        return false;
    }

    /**
     * 将对应位置 置为 0
     * @param val
     */
    public void reSet(int val) {
        if(val < 0) {
            throw new IndexOutOfBoundsException();
        }
        int arrayIndex = val / 8 ;
        int bitIndex = val % 8;
        elem[arrayIndex] &= ~(1 << bitIndex);
        usedSize--;
    }

    //当前位图中记录的元素的个数
    public int getUsedSize() {
        return usedSize;
    }

}

位图的应用:

  1. 快速查找某个数据是否在一个集合中
  2. 排序 + 去重
  3. 求两个集合的交集、并集等
  4. 操作系统中磁盘块标记

补充、常见位运算总结

a,基础运算符

常见位运算符:
右移:>>
左移:<<
取反 : ~
与 :& 有0就为0
或 : | 有1就为1
异或:^ 相同为0,相异为1/无进位相加

b,给定一个数n,确定他的二进制表示第x位是0还是1

最低位在最右边
n:0110101000 我想要知道第x位的是0还是1,只需要让这一位和1与一下就可以了,
0110101000
(n>>x)&1 这个结果就是n的第x位是0还是1,并且不会改变n的大小。

c,将一个数n的二进制表示第x位修改为1

n:0110101000
0000001000 将这两个数或一下就可以得到修改后的值,所以我们需要构造出一个第x为1,其他位为0的数字,也就是(1<<x)那么最终的公式是:
n=n|(1<<x) 或者写成这样也可以 n|=(1<<x)
可以对a=0进行改造得到我们想要的值

d,将一个数n的二进制表示第x位修改为0

n:0110101000
1111110111 将这两个数&一下就可以了,所以我们需要构造出来一个第x位为0,其他位为1的二进制数,也就是~(1<<x) 那么最终运算的公式是:
n&=(~(1<<x)) 或者写成这样也可以 n=n&(~(1<<x))

e,位图思想

本质就是哈希表,我们常见的哈希表是数组形式比如int[10]={0,1,2,3,4,5,6,7,8,9},根据下标可以在O(1)时间复杂度下找到arr[i],位图的思想就说:把一个数n的二进制表示整体看做一个哈希表,每一位0或者1可以表示两个状态。boolean数组或许就是这样。(未完待续…)

f,提取一个二进制数最右侧的1

这个怎么提取确实难想,直接上答案吧:
n&(-n) 这样一个数字就是成功提取了二进制数n的最右侧1,
n = 0110101000 ,对进行取反加1得到 -n:1001011000
n = 0110101000
-n = 1001011000
ret = n&(-n) = 0000001000 这个就是最终的提取结果,n不会被改变,这个结果需要一个变量来接收.

g, 将一个二进制数n最右侧的1改为0

这个算法按理讲应该可以想出来,我要改变最低为的1,就代表比这一位高的都不能变,比这一位低的都是0,那么n-1就会是一个高位和n一致,第位从1全变成1,要改变的这一位从1变成了0,那么n&(-n)就是最终结果;
n=0110101000
n-1=0110100111
n&(n-1)=0101010000 ok,这就成功把最右侧1改为了0

h,运算符的优先级

自己写的时候能加括号就加括号
在编程中,运算符的优先级决定了表达式中运算的执行顺序。下面是一些常见运算符的优先级:

  1. 括号 (): 最高优先级,表达式会首先被求值。
  2. 一元运算符: 如!-++--等。
  3. 算术运算符: */%+-。乘、除、取模的优先级高于加、减。
  4. 位运算符: ~&|^<<>>
  5. 关系运算符: <><=>===!=
  6. 逻辑运算符: &&||
  7. 赋值运算符: =+=-=*=/=%=等。

当表达式中含有多个运算符时,优先级高的运算符会先被求值。如果优先级相同,则从左到右依次求值。
我们可以使用括号来改变表达式的求值顺序。括号内的表达式会首先被求值。

没咋用过逗号运算符

例如:

5 + 3 * 2 // 结果是 11, 因为 * 的优先级高于 +
(5 + 3) * 2 // 结果是 16, 因为括号内的表达式先被求值

位运算符是一类特殊的运算符,它们直接对数字的二进制位进行操作。位运算符的优先级如下:

  1. 位非 (~): 最高优先级,对一个数的二进制位进行取反操作。
  2. 位与 (&): 对两个数的对应位进行"与"操作。
  3. 位或 (|): 对两个数的对应位进行"或"操作。
  4. 位异或 (^): 对两个数的对应位进行"异或"操作。
  5. 左移 (<<): 将一个数的二进制位向左移动指定的位数。
  6. 右移 (>>): 将一个数的二进制位向右移动指定的位数。

位运算符的优先级高于算术运算符,但低于一元运算符。
例如:

a = 5 & 3 // 结果为 1,因为 5 的二进制是 101,3 的二进制是 011,按位与得到 001,即 1
b = 5 | 3 // 结果为 7,因为 5 的二进制是 101,3 的二进制是 011,按位或得到 111,即 7
c = 5 ^ 3 // 结果为 6,因为 5 的二进制是 101,3 的二进制是 011,按位异或得到 110,即 6
d = 5 << 1 // 结果为 10,因为 5 的二进制是 101,左移 1 位得到 1010,即 10
e = 5 >> 1 // 结果为 2,因为 5 的二进制是 101,右移 1 位得到 10,即 2

理解位运算符的优先级和使用方法对于一些底层编程和优化很有帮助。

i,异或(^)运算的规律

1,a^0=a
2,a^a=0
3,abc=a(bc) 异或运算满足交换律,一大堆数异或在一起,计算结果和异或顺序无关。
前两个还是比较好理解的,最后一个特性也是与生俱来的,因为异或没有进位相加

PS:
7,8两小节可以帮助我们完成力扣这三道题目(191,338,461)
第9可以帮助我们完成力扣这两道题目(136,260)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

GaussDB DWS 详解

文章目录 GaussDB DWS 详解一、简介二、DWS的分布式架构架构概述关键组件 三、分布式查询数据查询流程SQL执行的示例 批注&#xff1a;本文引鉴了Forlogen博主的一些内容&#xff0c;并加以补充&#xff0c;以供学习了解。 GaussDB DWS 详解 一、简介 DWS(Data Warehouse Ser…

数据库-三范式

第一范式 1 数据库所有字段都只有单一属性。 2 单一属性由基本数据类型构成。 3 数据库的表都是二维的行与列。 例如上面的例子就不满足第一范式&#xff0c;因为是可以继续拆分的&#xff0c;拆分为更多的属性。 第二范式 1 符合第一范式 2 表必须有个主建 3 其它字段可以…

《0基础》学习Python——第十一讲__时间函数

一、时间函数是Python中的内置函数和模块&#xff0c;用于处理日期和时间相关的操作。以下是常用的时间函数的种类和用法&#xff1a; 1、time.time()&#xff1a;返回当前时间的时间戳。 时间戳&#xff08;timestamp&#xff09;是一种表示日期和时间的方式&#xff0c;它是一…

Linux--USB驱动开发(二)插入USB后的内核执行程序

一、USB总线驱动程序的作用 a&#xff09;识别USB设备 1.1 分配地址 1.2 并告诉USB设备(set address) 1.3 发出命令获取描述符 b&#xff09;查找并安装对应的设备驱动程序 c&#xff09;提供USB读写函数 二、USB设备工作流程 由于内核自带了USB驱动,所以我们先插入一个U…

CSS-0_3 CSS和单位

文章目录 CSS的值和单位属性值长度单位CSS和绝对单位CSS和相对单位百分比em & rem视口 颜色单位 碎碎念 CSS的值和单位 我们知道&#xff0c;CSS是由属性和属性值所组成的表 随着CSS的发展&#xff0c;属性不说几千也有几百&#xff0c;我从来不支持去背诵所有的可能性。…

K8S系列-Kubernetes基本概念及Pod、Deployment、Service的使用

一、Kubernetes 的基本概念和术语 一、资源对象 ​ Kubernetes 的基本概念和术语大多是围绕资源对象 Resource Object 来说的&#xff0c;而资源对象在总体上可分为以下两类: 1、某种资源的对象 ​ 例如节点 Node) Pod 服务 (Service) 、存储卷 (Volume&#xff09;。 2、…

Nginx入门到精通五(动静分离)

下面内容整理自bilibili-尚硅谷-Nginx青铜到王者视频教程 Nginx相关文章 Nginx入门到精通一&#xff08;基本概念介绍&#xff09;-CSDN博客 Nginx入门到精通二&#xff08;安装配置&#xff09;-CSDN博客 Nginx入门到精通三&#xff08;Nginx实例1&#xff1a;反向代理&a…

从0-1搭建一个web项目(页面布局详解)详解

本章分析页面布局详解详解 ObJack-Admin一款基于 Vue3.3、TypeScript、Vite3、Pinia、Element-Plus 开源的后台管理框架。在一定程度上节省您的开发效率。另外本项目还封装了一些常用组件、hooks、指令、动态路由、按钮级别权限控制等功能。感兴趣的小伙伴可以访问源码点个赞 地…

java数组之冒泡排序、快速排序

一、排序算法概述 1.算法定义 排序&#xff1a;假设含有n个记录的序列为{R1&#xff0c;R2&#xff0c;...,Rn},其相应的关键字序列为{K1&#xff0c;K2&#xff0c;...,Kn}。将这些记录重新排序为{Ri1,Ri2,...,Rin},使得相应的关键字值满足条Ki1<Ki2<...<Kin,这样的…

使用Keepalived实现双机热备(虚拟漂移IP地址)详细介绍

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f427;Linux基础知识(初学)&#xff1a;点击&#xff01; &#x1f427;Linux高级管理防护和群集专栏&#xff1a;点击&#xff01; &#x1f510;Linux中firewalld防火墙&#xff1a;点击&#xff01; ⏰️创作…

BI工具的AI革新:对话式分析如何引领企业智能转型?

在数据驱动的时代&#xff0c;数据分析早已成为企业决策制定的关键支撑。但是&#xff0c;很多企业在数字化转型的过程中&#xff0c;常常面临门槛高、流程复杂等问题。而AI技术的发展为解决上述问题带来了突破。 为了简化企业智能转型路径&#xff0c;帆软接入AI大模型技术&a…

Scherlokk - Mac 文件快速搜索对比工具

Scherlokk 是一款适用于 Mac 的文件内容快搜比较工具&#xff0c;在 Scherlokk 内输入关键词&#xff0c;即可在本地磁盘 / 移动硬盘 / 网络驱动器等区域内&#xff0c;查找包含该词的文件&#xff0c;快速定位所需文件&#xff0c;并提供文件比较、快速筛选过滤等功能。 两种…

SpringCloud--常用组件和服务中心

常用组件 Euroke和nacos 区别 负载均衡 负载均衡策略有哪些 自定义负载均衡策略

Power Apps使用oData访问表数据并赋值前端

在使用OData查询语法通过Xrm.WebApi.retrieveMultipleRecords方法过滤数据时&#xff0c;你可以指定一个OData $filter 参数来限制返回的记录集。 以下是一个使用Xrm.WebApi.retrieveMultipleRecords方法成功的例子&#xff0c;它使用了OData $filter 参数来查询实体的记录&am…

YOLOv5分类任务——手势识别

1. 下载YOLOv5官方代码 ONNX > CoreML > TFLite (github.com)">ultralytics/yolov5: YOLOv5 🚀 in PyTorch > ONNX > CoreML > TFLite (github.com) 2. 配置环境 打开终端,先建立名为YOLO5的环境,再将路径切换为requirements.txt文件夹所在的路径…

C#环境与数据类型

文章目录 C#环境.NET 框架集成开发环境 创建一个C#项目数据类型值类型引用类型对象类型object动态类型dynamic字符串类型string 指针类型 类型转换隐式转换显示转换&#xff08;强制转换&#xff09;C#提供的类型转换方法Convert类Parse方法TryParse方法 C#环境 .NET 框架 C#是…

Directory Opus 13 专业版(Windows 增强型文件管理器)值得购买?

在使用电脑时&#xff0c;总少不了和文件打交道。系统自带的 Explorer 资源管理器功能又非常有限&#xff0c;想要拥有一个多功能文件管理器吗&#xff1f; Directory Opus 是一款老牌多功能文件管理器&#xff0c;能很好地接管 Windows 资源管理器。 接管资源管理器 Directo…

Kotlin标准函数(语法糖)let with run also apply快速讲解

目录 1、知识储备——扩展函数 原理 定义扩展函数 调用扩展函数 2、返回值为上下文对象的标准函数 apply also 3、返回值为Lambda表达式结果 let run with 4、一表总结 1、知识储备——扩展函数 原理 Kotlin 在不继承父类或实现接口下&#xff0c;也能扩展一个类的…

硅谷甄选4(项目主体)

1.路由配置 1.1路由组件的雏形 src\views\home\index.vue&#xff08;以home组件为例&#xff09; 安装插件&#xff1a; 1.2路由配置 1.2.1路由index文件 src\router\index.ts //通过vue-router插件实现模板路由配置 import { createRouter, createWebHashHistory } fro…

【Qt 常用控件】

文章目录 1. Push Button 1. Push Button &#x1f427;给按钮设置图标