1.前言
先说一下什么是2-bitmap,为每个数分配2bit,00表示没有出现过,01表示出现过1次,10表示出现过2次及以上,11表示无意义。下面有个需求,求出一个数组中出现过一次的数字,显然用bitmap是无法实现的,位图法只能判断出是否出现过,但是只出现过一次是无法确定的,用2-bitmap正好能解决这种情况。
2.数据结构
既然连个bit表示一个数字,则能容纳的数字个数就是bitmap的一半
插入[1,4,3,7,7,6,10,15,30,15,7]后为
3.算法
这里有几个重要的地方,一是在插入一个数时要判断插入的数在数组中的状态,是没有出现过,还是出现过,二是状态的改变,未出现过,就将00变成01,如果出现过一次,就将01变成10,如果出现过多次,则不处理。
function bitMap2($arr) : array {
$bitmap = array_fill(0,50,0);
$int_bit_size = PHP_INT_SIZE * 4;
foreach ($arr as $item){
$bytePos = $item / $int_bit_size;
$bitPos = $item % $int_bit_size;
$status = findPosBitMapStatus($bitmap, $item);
$position = 0;
switch ($status){
case 0 :{
$position = 1 << ($bitPos*2);
};
break;
case 1 :{
$position = 2 << ($bitPos*2);
};
break;
case 2 :;
}
//将对应为上的数值先清零,但是要保证其他位置上的数不变
$bitmap[$bytePos] &= ~(3<<($bitPos*2));
//将对应位上的二进制位改变成00,01,或10
$bitmap[$bytePos] |= $position;
}
return $bitmap;
}
//查看这个数在2位图法中的状态,返回0,1,2,代表位出现过,出现过1次,出现过2次及以上。
function findPosBitMapStatus($bitMap, $number) : int {
$int_bit_size = PHP_INT_SIZE * 4;
$bytePos = $number / $int_bit_size;
$bitPos = $number % $int_bit_size;
$res = $bitMap[$bytePos]>>($bitPos*2) & 3;
return $res;
}
//输出
function outPut2($bitMap){
$int_bit_size = PHP_INT_SIZE * 4;
foreach ($bitMap as $k=>$item){
for ($i=0;$i<$int_bit_size;$i++){
$flag = $item>>($i*2) & 3;
if ($flag){
$res = $k * $int_bit_size + $i;
echo $res;
echo "出现了".$flag."次";
echo "<br>";
}
}
}
}
$test_arr = array(1,4,3,7,7,6,10,15,30,15,7); //定义一个乱序的数组
$temp = bitMap2($test_arr);
outPut2($temp);
4.题
用位图法的好处就是能节约内存,比如这个题
在2.5亿个整数中找出不重复的整数,注,内存不足以容纳这2.5亿个整数。这个就可以采用2-bitmap来解决这道题。