数据结构与算法(位运算)

位运算

内存中的数据,最终的存储方式都是二进制,位运算就是对整数在内存的二进制位进行操作。

 

按位与 &

两个整数进行按位与运算,相同二进制位的数字如果都是是,则结果为1,有一个为0,则结果为0

下面是 3 & 7 的计算过程

二进制                整数

0  1  1                   3

1  1  1                   7

0  1  1                   3(结果)

3 & 7 = 3

 

按位或 |

两个整数进行按位或运算,相同二进制位的数字如果有一个为1,则结果为1,都为0,则结果为0

下面是 5 | 8 的计算过程

二进制                整数

0  1  0  1               5

1  0  0  0               8

1  1  0  1              13(结果)

5 | 8 = 13

 

左移 <<

二进制向左移动n位,在后面添加n个0

下面的 3 << 1 的计算过程

二进制                整数

      1  1                 3

  1  1  0                 6

3<<1 = 6

 

练习:一组数,内容为 3,9,19,20 ,请用一个整数来表示这四个数

var value = 0
value = value | 1<<3
value = value | 1<<9
value = value | 1<<19
value = value | 1<<20
console.log(value)

程序输出结果为:1573384

 

bitmap

新的实现方式

经过前面一系列的分析和位运算学习,现在我们要重新设计一个类,实现 addMember 和 isExist 方法,用更快的速度,更少的内存

  • 数据范围是0~100,那么只需要4个整数就可以表示 4 * 32 个数的存在与否(如果用普通的数组方法,需要数组中有100个数来表示0-100的存在与否),创建一个大小为4的数组
  • 执行addMember时,先用 member/32,确定member在数组里的索引(arr_index),然后用 member%32,确定在整数的哪个二进制位进行操作(bit_index),最后执行 bit_arr[arr_index] = bit_arr[arr_index] | 1<<bit_index
  • 执行isExist时,先用 member/32,确定member在数组里的索引(arr_index),然后用 member%32,确定在整数的哪个二进制位进行操作(bit_index),最后执行 bit_arr[arr_index] & 1<<bit_index ,如果结果不为 0 ,就说明 member存在
function BitMap(size){
  var bit_arr = new Array(size)
  for(var i=0; i<bit_arr.length; i++){
    bit_arr[i] = 0
  }

  this.addMember = function(member){
    // 决定在数组中的索引
    var arr_index = Math.floor(member/32) 
    // 决定在整数的32个bit位的哪一位上
    var bit_index = member%32 
    bit_arr[arr_index] = bit_arr[arr_index] | 1<<bit_index
  }

  this.isExist = function(member){
    // 决定在数组中的索引
    var arr_index = Math.floor(member/32) 
    // 决定在整数的32个bit位的哪一位上
    var bit_index = member%32 

    var value = bit_arr[arr_index] & 1<<bit_index
    if(value !== 0){
      return true
    }
    return false
  }
}

 

概念

这种数据结构基于位做映射,能够用很少的内存存储数据,和数组不同,它只能存储表示某个数是否存在,可以用于大数据去重,大数据排序,两个集合取交集。

BitMap在处理大数据时才有优势,而且要求数据集紧凑,如果要处理的数只有3个:1,1000,100000,那么空间利用率太低了,最大的值决定了BitMap要用多少内存。

 

大数据排序

有多达10亿个无序整数,已知最大值为15亿,请对这10亿个数进行排序。(BitMap做排序有个前提,就是数据是不能重复的,如果有重复的就做不成了)

传统排序算法都不可能解决这个排序问题,即便内存允许,其计算时间也是漫长的,如果使用BitMap就极为简单。

BitMap存储最大值为15亿的集合,只需要180M 的空间,空间使用完全可以接受,至于速度,存储和比较过程中的位运算速度都非常快,第一次遍历,将10亿个数都放入到BitMap中,第二次,从0到15亿进行遍历,如果在BitMap,则输出该数值,这样经过两次遍历,就可以将如此多的数据排序。

 

为了演示方便,只用一个很小的数组,[0, 6, 88, 7, 73, 34, 10, 99, 22],已知数组最大值是 99 ,利用BitMap排序的算法如下

var arr = [0, 6, 88, 7, 73, 34, 10, 99, 22]
var sort_arr = []

var bit_map = new BitMap(4)
for(var i=0; i<arr.length; i++){
  bit_map.addMember(arr[i])
}

for(var i=0; i<=99; i++){
  if(bit_map.isExist(i)){
    sort_arr.push(i)
  }
}
console.log(sort_arr)

输出结果:[ 0, 6, 7, 10, 22, 34, 73, 88, 99 ]

 

 

布隆过滤器

前面所讲的BitMap的确很厉害,可以,却有着很强的局限性,BitMap只能用来处理整数,无法处理字符串,假设让你写一个强大的爬虫,每天爬取数以亿计的网页,那么你就需要一种数据结构,能够存储你已经爬取过的 url ,这样,才不至于重复爬取。

你可能会想到使用hash函数对url进行处理,转成整数,这样,似乎又可以使用 BitMap 了,但这样还是会有问题。假设BitMap能够映射的最大值是 M ,一个url的hash值需要对M求模,这样,就会产生冲突,而且随着存储数据的增多,冲突率会越来越大。

布隆过滤器的思想非常简单,其基本思路和BitMap是一样的,可以把布隆过滤器看做是 BitMap的扩展。为了解决冲突率,布隆过滤器要求使用k个 hash函数,新增一个 key时,把 key散列成 k 个整数,然后在数组中将这 k 个整数所对应的二进制位设置为1,判断某个key是否存在时,还是使用 k 个hash函数对key进行散列,得到k个整数,如果这k个整数所对应的二进制位都是1,就说明这个key存在,否则,这个key不存在。

 

对于一个布隆过滤器,有两个参数需要设置,一个是预估的最多存放的数据的数量,一个是可以接受的冲突率。

 

 

 

hash函数

哈希函数就是将某一个不定长的对象映射为另一个定长的对象,如果你对这个概念感到困惑,你就换一个理解方法,你给hash函数传入一个字符串,它返回一个整数。为了实现一个布隆过滤器,我们需要一个好的 hash函数,计算快,冲突又少

 

posted @ 2019-11-18 15:31  落叶无痕~  阅读(726)  评论(0编辑  收藏  举报