🌷一、位向量
☘️1.问题
我们先看一个问题,如果我们有一千万个不重复的数,现在要对这一千万个数进行排序。
🥇解决办法
每一个数都用int
来表示,然后把一千万数都读到内存中,然后进行排序。
🥈消耗内存
首先一个int
类型是四个字节,那么一千万个数就是:10000000 * 4B = 39062.5KB = 38M
🌱2.位向量
位向量简单解释就是由二进制0和1组成的向量
那么如何用位向量来表示数呢?
假设现在有一个向量[0, 1, 0, 1, 0] ,从右往左看第1、3 都是1,那么这个位向量就表示:1、3两个数,并且是有序的,从右到左依次递增。
🥇解决办法
同样还是上面那个问题,还是一千万个数,将这一千万个数用位向量表示,这样就会自动进行排序,除此之外还可以节省内存。
🥈消耗内存
一个int
是4字节也就是32位,能表示32个数,一千万个数就是:1000 0000 / 32 = 312500 * 4B = 1220KB = 1.2M
🌸二、位向量操作
假设现在有60个数,要用位向量来表示,那么60个数,一个int
能表示32个数,所以需要长度为(60 / 32) + 1 的int
数组来表示。如下图,将60个数放到下面长度为2的int数组中int[] a = new int[2]
🥝1.定位元素位置
🍑1.1 理论过程
无论是往位向量中放元素,还是从位向量中删除元素或者判断位向量中是否有某个元素,都需要定位到这个元素在位向量中的位置。
这个位置指两个位置:第一个是在数组索引的位置;第二个是在这个索引处int
的bit
位。当然这里也不只是int
类型,也可以是long
类型,如果是long
类型那么每一个数组元素就可以表示64个元素。后续的位向量都用int
类型来示例。
首先是确定放在数组的哪一个位置,首先一个int
是32位,那么数组的每一个元素能表示的数据范围如下
a[1] = [0, 31]
a[2] = [32, 63]
a[3] = [64, 95]
那么可以推断出,判断一个元素放在数组的那个位置,就是用这个元素除以数组数据类型的长度的到的商就是这个元素在数组中的位置,这里int
的长度是32,那么就用这个元素来除以32就可以定位到这个元素在数组的索引位置。
那么具体在哪个bit
位,由于每个元素都不一样,那么在一个int
长度内的元素的余也就不一样,如果余数一样,那么商肯定不一样。所以定位bit
最简单的就余数是多少,那么该元素的bit
位置就是多少。
🍓1.2 计算过程
假设我们要操作的元素是m
,那么首先来计算它除以32的商和余。
根据上面这个公式计算,商是p
余数是s
。所以元素m在数组的索引是p
,在bit
的位置是s
。
这里还有个问题就是p
可以用a[p]
就能表示。
那么s
怎么表示,最简单的做法就是移位操作,让1来进行左移s
位就能把元素在bit
位上表示出来。这里还有个问题就是如果是1左移s
位,是不是就是多了一位,其实这里之所以左移s
而不是s-1
是因为刚好整除的情况,如果元素m
刚好能整除32,那么余就是0,1 << 0刚好是在第一位。
所以,元素在位向量中的定位:
🥇数组索引位置可以用a[p]
来表示
🥈**bit
位可以用1 << s
来表示**
🍑 1.3实际操作
假设m
就是37,首先计算它的商和余,来确定位置
- 计算商
由上面的推导公式可知,计算商就是将37进行右移动5位
37的二进制
右移动5位之后结果为1,也就是商为1
- 计算余数
也就是计算37 & (2^5 - 1)
37的二进制
2^5 - 1 的二进制
37 & (2^5 - 1)
通过上面的计算得到余数是5,该元素在bit
位上的表示为 1<< 5
1 << 5
所以37在位向量中的位置:索引位置位a[1];bit
位置位 1<<5。
🌲1.放数据
🎃1.1 理论过程
定位到数据的位置之后,怎么将数据放到位向量中,首先数组索引位置a[p]
是确定的,bit
的位置是1<<s
假设除了该bit
位,其它的bit
位也有数据,放数据的原则就是将本bit
位设置为1,其它位置保持不变。
那么就用1 << s
和bit
位其它的位置做一个或运算,其它位置如果是1,那么进行或运算也不会改变,而且1 << s
进行或运算无论这个位置以前是什么,和1 << s
进行或运算后都会变成1的。
🏔️1.2 实际操作
a[1] 的32个bit
位原来可能有值,所以要进行或运算将新的37放进去,这里是第一次放元素,所以a[1]
是0,然后进行或运算,然后将值赋值给a[1]
,这样就完成了整个操作
a[1] = a[1] | (1 << 5)
🌳2.删除数据
🎋2.1 理论过程
删除元素的过程也很简单,就是将对应的bit
位置为0,首先该元素的bit
位置是1<<s
,那么删除元素,就是将该位置的1变为0,其它位置保持不变。
首先将1<<s
取反,也就是~运算,这样1 << s
位置就变成0了,其它位置就变成1了,然后再和索引处的值进行与运算,其它位置的值和1进行与运算都不会改变成,而1 << s
变成0,做与运算无论这个位置以前是什么值,和0做与运算之后都会变成0
🍅2.2 实际操作
首先取反
~(1 << 5)
然后进行与运算
a[1] = a[1] & ~(1 << 5)
🌴3.判断数据
🍇3.1 理论过程
如果某一个数据在位向量中的bit
位是1那么表示该位向量中有该元素,如果是0表示该位向量中没有该元素。
判断1<<s
是否有元素,就将其它位置的元素全部变成0,该位置保持不动。那么要将其它位置变成0,就和0进行与运算。
🍈3.2 实际操作
1 << 5做与运算,将其它位置值置为0
a[1] & (1 << 5)