Python: 实现bitmap数据结构

bitmap是很经常使用的数据结构,好比用于Bloom Filter中、用于无重复整数的排序等等。bitmap一般基于数组来实现,数组中每一个元素能够当作是一系列二进制数,全部元素组成更大的二进制集合。对于Python来讲,整数类型默认是有符号类型,因此一个整数的可用位数为31位。python

bitmap实现思路

bitmap是用于对每一位进行操做。举例来讲,一个Python数组包含4个32位有符号整型,则总共可用位为4 * 31 = 124位。若是要在第90个二进制位上操做,则要先获取到操做数组的第几个元素,再获取相应的位索引,而后执行操做。shell

上图所示为一个32位整型,在Python中默认是有符号类型,最高位为符号位,bitmap不能使用它。左边是高位,右边是低位,最低位为第0位。数组

初始化bitmap

首先须要初始化bitmap。拿90这个整数来讲,由于单个整型只能使用31位,因此90除以31并向上取整则可得知须要几个数组元素。代码以下:数据结构

#!/usr/bin/env python
#coding: utf8

class Bitmap(object):
	def __init__(self, max):
		self.size = int((max + 31 - 1) / 31) #向上取整

if __name__ == '__main__':
	bitmap = Bitmap(90)
	print '须要 %d 个元素。' % bitmap.size

$ python bitmap.py
须要 3 个元素。

计算索引

肯定了数组大小后,也就能够建立这个数组了。若是要将一个整数保存进这个数组,首先须要知道保存在这个数组的第几个元素之上,而后要知道是在这个元素的第几位上。所以计算索引分为:
app

  1. 计算在数组中的索引测试

  2. 计算在数组元素中的位索引spa

计算在数组中的索引

计算在数组中的索引实际上是跟以前计算数组大小是同样的。只不过以前是对最大数计算,如今换成任一须要存储的整数。可是有一点不一样,计算在数组中的索引是向下取整,因此须要修改calcElemIndex方法的实现。代码改成以下:
code

#!/usr/bin/env python
#coding: utf8

class Bitmap(object):
	def __init__(self, max):
		self.size  = self.calcElemIndex(max, True)
		self.array = [0 for i in range(self.size)]

	def calcElemIndex(self, num, up=False):
		'''up为True则为向上取整, 不然为向下取整'''
		if up:
			return int((num + 31 - 1) / 31) #向上取整
		return num / 31

if __name__ == '__main__':
	bitmap = Bitmap(90)
	print '数组须要 %d 个元素。' % bitmap.size
	print '47 应存储在第 %d 个数组元素上。' % bitmap.calcElemIndex(47)

$ python bitmap.py
数组须要 3 个元素。
47 应存储在第 1 个数组元素上。

因此获取最大整数很重要,不然有可能建立的数组容纳不下某些数据。
排序

计算在数组元素中的位索引

数组元素中的位索引能够经过取模运算来获得。令需存储的整数跟31取模便可获得位索引。代码改成以下:
索引

#!/usr/bin/env python
#coding: utf8

class Bitmap(object):
	def __init__(self, max):
		self.size  = self.calcElemIndex(max, True)
		self.array = [0 for i in range(self.size)]

	def calcElemIndex(self, num, up=False):
		'''up为True则为向上取整, 不然为向下取整'''
		if up:
			return int((num + 31 - 1) / 31) #向上取整
		return num / 31

	def calcBitIndex(self, num):
		return num % 31

if __name__ == '__main__':
	bitmap = Bitmap(90)
	print '数组须要 %d 个元素。' % bitmap.size
	print '47 应存储在第 %d 个数组元素上。' % bitmap.calcElemIndex(47)
	print '47 应存储在第 %d 个数组元素的第 %d 位上。' % (bitmap.calcElemIndex(47), bitmap.calcBitIndex(47),)

$ python bitmap.py
数组须要 3 个元素。
47 应存储在第 1 个数组元素上。
47 应存储在第 1 个数组元素的第 16 位上。

别忘了是从第0位算起哦。

置1操做

二进制位默认是0,将某位置1则表示在此位存储了数据。代码改成以下:

#!/usr/bin/env python
#coding: utf8

class Bitmap(object):
	def __init__(self, max):
		self.size  = self.calcElemIndex(max, True)
		self.array = [0 for i in range(self.size)]

	def calcElemIndex(self, num, up=False):
		'''up为True则为向上取整, 不然为向下取整'''
		if up:
			return int((num + 31 - 1) / 31) #向上取整
		return num / 31

	def calcBitIndex(self, num):
		return num % 31

	def set(self, num):
		elemIndex = self.calcElemIndex(num)
		byteIndex = self.calcBitIndex(num)
		elem      = self.array[elemIndex]
		self.array[elemIndex] = elem | (1 << byteIndex)

if __name__ == '__main__':
	bitmap = Bitmap(90)
	bitmap.set(0)
	print bitmap.array

$ python bitmap.py
[1, 0, 0]

由于从第0位算起,因此如须要存储0,则须要把第0位置1。

清0操做

将某位置0,也即丢弃已存储的数据。代码以下:

#!/usr/bin/env python
#coding: utf8

class Bitmap(object):
	def __init__(self, max):
		self.size  = self.calcElemIndex(max, True)
		self.array = [0 for i in range(self.size)]

	def calcElemIndex(self, num, up=False):
		'''up为True则为向上取整, 不然为向下取整'''
		if up:
			return int((num + 31 - 1) / 31) #向上取整
		return num / 31

	def calcBitIndex(self, num):
		return num % 31

	def set(self, num):
		elemIndex = self.calcElemIndex(num)
		byteIndex = self.calcBitIndex(num)
		elem      = self.array[elemIndex]
		self.array[elemIndex] = elem | (1 << byteIndex)

	def clean(self, i):
		elemIndex = self.calcElemIndex(i)
		byteIndex = self.calcBitIndex(i)
		elem      = self.array[elemIndex]
		self.array[elemIndex] = elem & (~(1 << byteIndex))

if __name__ == '__main__':
	bitmap = Bitmap(87)
	bitmap.set(0)
	bitmap.set(34)
	print bitmap.array
	bitmap.clean(0)
	print bitmap.array
	bitmap.clean(34)
	print bitmap.array

$ python bitmap.py
[1, 8, 0]
[0, 8, 0]
[0, 0, 0]

清0和置1是互反操做。

测试某位是否为1

判断某位是否为1是为了取出以前所存储的数据。代码以下:

#!/usr/bin/env python
#coding: utf8

class Bitmap(object):
	def __init__(self, max):
		self.size  = self.calcElemIndex(max, True)
		self.array = [0 for i in range(self.size)]

	def calcElemIndex(self, num, up=False):
		'''up为True则为向上取整, 不然为向下取整'''
		if up:
			return int((num + 31 - 1) / 31) #向上取整
		return num / 31

	def calcBitIndex(self, num):
		return num % 31

	def set(self, num):
		elemIndex = self.calcElemIndex(num)
		byteIndex = self.calcBitIndex(num)
		elem      = self.array[elemIndex]
		self.array[elemIndex] = elem | (1 << byteIndex)

	def clean(self, i):
		elemIndex = self.calcElemIndex(i)
		byteIndex = self.calcBitIndex(i)
		elem      = self.array[elemIndex]
		self.array[elemIndex] = elem & (~(1 << byteIndex))

	def test(self, i):
		elemIndex = self.calcElemIndex(i)
		byteIndex = self.calcBitIndex(i)
		if self.array[elemIndex] & (1 << byteIndex):
			return True
		return False

if __name__ == '__main__':
	bitmap = Bitmap(90)
	bitmap.set(0)
	print bitmap.array
	print bitmap.test(0)
	bitmap.set(1)
	print bitmap.test(1)
	print bitmap.test(2)
	bitmap.clean(1)
	print bitmap.test(1)

$ python bitmap.py
[1, 0, 0]
True
True
False
False

接下来实现一个不重复数组的排序。已知一个无序非负整数数组的最大元素为879,请对其天然排序。代码以下:

#!/usr/bin/env python
#coding: utf8

class Bitmap(object):
	def __init__(self, max):
		self.size  = self.calcElemIndex(max, True)
		self.array = [0 for i in range(self.size)]

	def calcElemIndex(self, num, up=False):
		'''up为True则为向上取整, 不然为向下取整'''
		if up:
			return int((num + 31 - 1) / 31) #向上取整
		return num / 31

	def calcBitIndex(self, num):
		return num % 31

	def set(self, num):
		elemIndex = self.calcElemIndex(num)
		byteIndex = self.calcBitIndex(num)
		elem      = self.array[elemIndex]
		self.array[elemIndex] = elem | (1 << byteIndex)

	def clean(self, i):
		elemIndex = self.calcElemIndex(i)
		byteIndex = self.calcBitIndex(i)
		elem      = self.array[elemIndex]
		self.array[elemIndex] = elem & (~(1 << byteIndex))

	def test(self, i):
		elemIndex = self.calcElemIndex(i)
		byteIndex = self.calcBitIndex(i)
		if self.array[elemIndex] & (1 << byteIndex):
			return True
		return False

if __name__ == '__main__':
	MAX = 879
	suffle_array = [45, 2, 78, 35, 67, 90, 879, 0, 340, 123, 46]
	result       = []
	bitmap = Bitmap(MAX)
	for num in suffle_array:
		bitmap.set(num)
	
	for i in range(MAX + 1):
		if bitmap.test(i):
			result.append(i)

	print '原始数组为:    %s' % suffle_array
	print '排序后的数组为: %s' % result

$ python bitmap.py
原始数组为:   [45, 2, 78, 35, 67, 90, 879, 0, 340, 123, 46]
排序后的数组为:[0, 2, 35, 45, 46, 67, 78, 90, 123, 340, 879]

结束语

bitmap实现了,则利用其进行排序就很是简单了。其它语言也一样能够实现bitmap,但对于静态类型语言来讲,好比C/Golang这样的语言,由于能够直接声明无符号整型,因此可用位就变成32位,只需将上述代码中的31改为32便可,这点请你们注意。

相关文章
相关标签/搜索