<BitMap>大名鼎鼎的bitmap算法

BitMap

抛砖引玉

  • 首先,我们思考一个问题:如何在3亿个整数(0~2亿)中判断某一个数是否存在?现在只有一台机器,内存只有500M
  • 这个问题像不像我们之前提到过的一个在0-10个数中,判断某一个数是否存在的问题呢?当时我们采取的做法是,建立一个长度是11的数组,下标从0开始,如果1存在则data[1] = 1,数字作为数组的下标,若该数字存在则在data[数字] = 1,将其赋值为1。那么我们这个是否可以这么做呢?
  • 明显不行。为什么呢?因为我们如果判断2亿个数字是否存在,建立一个2亿长度的数组,疯了么?想想就知道不可能。那么我们应该怎么做呢?
  • 这时候我们就需要变换我们的思路了,一个int类型整型,我们占用4个字节,一个字节我们占用8位:1 byte = 8 bit4 byte = 32 bit那么,我们如果将这2亿个数据不按照int的形式存储,而是按照位的形式存储,我们原来存储一个数字(4个字节)现在不就是可以存储32个数字(4个字节 = 32位)了么。秒啊~

什么是BitMap

  • 位图(BitMap),又称作栅格图或者点阵图,是使用像素阵列来表示的图像---------来自百度百科。
  • 明显上面说的什么,我们也不清楚,反正很高深了就是,其实我觉得简单理解就是我们用位(bit)来操作数据,以此来操作大数据量
  • 至于我们为什么用位来操作呢?因为计算机最小的单位就是bit(位)

类型的基础讲解

常用的数据类型和位的转换

1 byte = 8 bit

1 int = 4 byte = 32 bit

1 float = 4 byte = 32 bit

1 long = 8 byte = 64 bit

1 char = 2 byte = 16 bit

  • 我们这里就不做详细的解释,只要记住:1 byte = 8 bit,即1字节 = 8位

常用的位操作运算符

  • 我们这里讲述0. 常用的6种常用的位运算符(1)按位或(2)按位与(3)按位异或(4)求反(5)左移运算(6)右移运算

按位或运算

  • 按位或运算符,记做“|”,是一个双目运算符。
  • 简单来说就是二进制的位中只要有一个是1,其计算结果就是1,否则就是0
  • 举例说明:

    11 | 7

    11 0000 0000 0000 0000 0000 0000 0000 1011
    |                
    7 0000 0000 0000 0000 0000 0000 0000 0111
    结果 0000 0000 0000 0000 0000 0000 0000 1111

    11 | 7 = 1011 | 0111 = 1111 = 15

按位与运算

  • 按位与运算符,记做“&”,是一个双目运算符。
  • 简单来说就是二进制的位中只有都是1,才会是1,否则就是0
  • 举例说明:11 & 7
    11 0000 0000 0000 0000 0000 0000 0000 1011
    &                
    7 0000 0000 0000 0000 0000 0000 0000 0111
    结果 0000 0000 0000 0000 0000 0000 0000 0011

    11 &7 = 1011 | 0111 = 0011 = 3

按位异或运算

  • 按位异或运算符,记做“^”,是一个双目运算符。
  • 简单来说就是二进制的位中只要两个数字不一样就是1,否则就是0
  • 举例说明:

    11 ^ 7

    11 0000 0000 0000 0000 0000 0000 0000 1011
    ^                
    7 0000 0000 0000 0000 0000 0000 0000 0111
    结果 0000 0000 0000 0000 0000 0000 0000 1100

    11 &7 = 1011 | 0111 = 1100 = 12

    按位异或运算

public class Main1 {

    public static void main(String[] args) {
        System.out.println("11 | 7 的结果是 : "+(11|7));
        System.out.println("11 & 7 的结果是 :"+(11&7));
        System.out.println("11 ^ 7 的结果是 :"+(11^7));
    }
?
}
//----------------------------输出如下------------------------------
11 | 7 的结果是 : 15
11 & 7 的结果是 :3
11 ^ 7 的结果是 :12

按位求反运算

  • 按位求反运算符,记做“~”,是一个单目运算符。
  • 简单来说就是二进制的位中,原来是1,结果就是0,原来是0,结果就是1

左移运算

  • 左移运算符“<<”是一个双目运算符,其功能就是把"<<"左边的运算数的各个二进位全部左移若干位。
  • 举例说明:5 << 4
    5 0000 0000 0000 0000 0000 0000 0000 0101
    <<4                
    结果 0000 0000 0000 0000 0000 0000 0101 000

    5 << 4 = 0101 << 4 = 0101 0000 = 5* 2^4 = 5*16 =80

右移运算

  • 右移运算符 ">>" 是一个双目运算符,其功能就是把">>"左边的运算数的各个二进位全部右移若干位。
  • 举例说明:16 >> 3
    16 0000 0000 0000 0000 0000 0000 0001 0000
    >>3                
    结果 0000 0000 0000 0000 0000 0000 0000 0010

    16 >> 3 = 0001 0000 >> 3 = 0010 = 16 / 2^3 = 16/8 =2

如何巧妙的运用

  1. a >> x

这个就可以记做是:a / 2 ^ x比如说:8 >> 2 = 8 / 2 ^ 2 = 8 / 4 = 2

  1. a << x

这个就可以记做是: a * 2 ^ x比如说: 8 << 2 = 8 * 2 ^ 2 = 8 * 4 = 32

  1. a % 2^n

这个就可以记做是:a & 2^n-1比如说:7 % 4 = 7 & (4-1) = 3

实际问题分析

问题描述

  • 现在我们假设我们有64个数字,最大的数字就是64,那么我们用Bitmap的思想应该怎么解决呢?

分析

  1. 首先我们这里用int的数组来进行存储,一个int是4个字节,1个字节是8位,因此一个int型的数字可以存32位
  2. 我们这里最大的数是64
数组 存储位数 能存多少
data[0] 0-31 32位
data[1] 32-63 32位
data[2] 64-95 32位

由此可以推到出来:我们存取n个数字,其中最大的数字是MAX,则需要data[MAX/32 + 1]长度的数组,这里的32是因为1 int = 4 字节 = 32位


假设我们需要判断64是否在列表中(以int数组存储),我们就应该这样来计算

  1. 第一步我们判断声明多大的int型的数组

因为我们最大的数是64,所以根据我们的公式:data[MAX/32 + 1] ,由此可以计算出,我们声明的数组长度是data[64/32 + 1] = data[3],也就是说我们应该声明数组长度是3,即data[0] data[1] data[2]

  1. 先定位到数组,判断是第几个数组中存储着

因为我们一个数组长度是1个int,就是32位,查询的数字是64,所以64 / 32 = 2,因此定位到了data[2]

  • 这里除以32是因为,我们以int数组为例,1 int = 32 bit
  1. 再定位这个元素是数组中的第几位

同理,我们一个数组的长度是一个int,也就是4个字节,32位,查询的数字是64,64 % 32 = 0,因此定位到了我们是存在data[2]数组中的第0位

  • 这里取余32是因为,我们是以int数组为例,1 int = 32 bit
  1. 此时我们只需要判断data[2]数组中的第0位是否为1,为1表示已经存在,否则就是不存在。

这就是BitMap的算法的核心思想

公式小结

  1. 我们以int数组为例,一个int占用4个字节,也就是32位,存储数据的最大值是MAX(比如存1-2000,最大值就是2000)
  • 判断总共需要多大的int数组

MAX/32 + 1

  • 判断当前这个数字n在第几个数组中

n / 32

  • 判断当前这个数字n在数组中的第几位上

n % 32

  • 我们上面的32就是int占用的位数,可以换成其他的类型,如果用byte数组,则将32换成8,因为1个byte是一个字节,是8位。

计算存储空间

  • 假设我们现在要存储2亿个数字,如果直接用int数组来存,一个int是一个数字,则需要:2亿 * 4字节 / 1024 / 1024 = 762M
  • 假设我们现在用BitMap的思想存储,也是使用int数组,只不过一个int存储32个数组,则需要:2亿 / 32 + 1(需要开的int数组) * 4字节 / 1024 / 1024 = 23M

看看,是不是空间就是这么省下来了。

实战举例

题目

  • 假设我们有序列 2 9 33 12 11 65 14 , 我们开一个int类型的数组,将其存储进去,然后判断其是否存在,并可以实现某一个数字的删除,并用位运算符实现。

分析

一: 首先我们通过上面的三个公式已经可以很快的知道数组定义多大,在哪个数组中放值(哪一个数组,数组中的哪一位),但是这里我们的要求是使用位运算符,这时候结合我们最开始讲的位运算符将其进行简单的转换

  1. 判断数组定义多大
  • a / 2^n = a >> ndata[MAX/32 + 1] = data[MAX / 2 ^ 5 + 1] = data[MAX >> 5 + 1]
  1. 数字n存在哪一个数组中
  • a / 2^n = a >> ndata[n/32] = data[n / 2 ^ 5] = data[n >> 5]
  1. 数字n在数组的哪一位
  • a % 2^n = a & 2^n - 1data[n%32] = data[n % 2 ^ 5] = data[n & (2 ^ 5 -1)] = data[n & 31]

二: 其次我们在存入数组中需要将数组的第loc位由0变为1

将下标为loc的位由0变为1,可以把1左移loc位,然后使用或操作(只要有1个为1就是1)

  • 所以新增元素公式就是: data[X] = data[X] | (1 << loc),loc为当前数组的哪一位,data[X]就是当前的数组

三: 下一个问题就是我们需要查找一个数字是否已经存在

这时候我们就可以考虑&运算符,即都是1才是1,否则就是0判断下标loc的位是否已经存在,可以把1左移loc位,然后做与操作(都为1才是1,否则是0)

  • 如下图,判断loc位上是否存在:(下图表示已经存在)
  • 如下图,判断loc位上是否存在:(下图表示不存在)
  • 所以查找元素公式就是:0 == data[X] & 1 << loc,loc为当前数组的哪一位 ,这时候用0判断是因为我们除了移位过去的那位上的数字是1,其他位上的数字都是0,所以如果最后结果是0,表示就是不存在,否则就是存在

四: 最后一个问题就是我们要删除一个数字呢?就是将这个数字由1变为0(这里我们假设删除的数字是肯定存在的,不允许做不存在就删除

  • 这时候考虑我们异或操作:相同则为0,不同则为1,那如果我当前位上存在则肯定是1,如果我拿一个当前位是1的数和其做异或操作不就可以了假设我们需要删除下标是lco上的数我们的异或操作是相同为0,不同为1,所以即使数组原来位置上有1,我们也不用害怕,因为原来位子上是1,但是我们1进行左移loc后,除了loc上的数是1,其他位上的数肯定是0,参考上面的图解
  • 所以删除元素公式:data[X] = data[X] ^ (1 << loc),loc为当前数组的哪一位

代码实现



package com.demo.bitsmap;
?
public class Main2BitsMap {

    private int[] vals;

    public Main2BitsMap(int size) {
        this.vals = new int [size];
    }

    public static void main(String[] args) {
        int [] t = {1,2,3,4,5,33,34,45,77,108};
        Main2BitsMap map = new Main2BitsMap((108>>5)+1);
        for (int i :t) {
            map.add(i);
        }
        map.print();
        map.printBirany();
        System.out.println("添加数据109");
        map.add(109);
        System.out.println("是否包含 77 这个数据 ? " +map.find(77));
        System.out.println("是否包含 109 这个数据 ? " +map.find(109));
        System.out.println("删除数据77");
        map.delete(77);
        System.out.println("是否包含 77 这个数据 ? " +map.find(77));
    }

    public void add(int t) {

        int index  = t>>5;

        int loc = t&31;

        vals[index] = vals[index] | 1 << loc;
    }

    public boolean find(int t) {
        int index  = t>>5;

        int loc = t&31;

        int result = (vals[index]>>loc ) &1;

        return result ==0 ?false:true;
    }
    public boolean delete(int t ) {
        int index  = t>>5;

        int loc = t&31;

        if(!find(t)) {
            return false;
        }
        vals[index] = vals[index] ^ (1<<loc);

        return true;

    }

    public  void print() {
        for (int i : vals) {
            System.out.print(i+" ");
        }
    }

    public void printBirany() {
        int index =0;
        System.out.println();
        for (int i : vals) {
            for (int j = 31; j >=0; j--) {
                System.out.print((i>>j)&1 );
                index++;
                if(index==5) {
                    System.out.print(" ");
                    index=0;
                }

            }
            index =0;
            System.out.println();

        }
    }
}
?

结果显示

62 8198 8192 4096 00000 00000 00000 00000 00000 01111 1000000 00000 00000 00010 00000 00001 1000000 00000 00000 00010 00000 00000 0000000 00000 00000 00001 00000 00000 00添加数据109是否包含 77 这个数据 ? true是否包含 109 这个数据 ? true删除数据77是否包含 77 这个数据 ? false?

总结

  • BitMap的时间复杂度是O(1)
  • BitMap的优点(1)可以解决数据判重的问题(2)可以对没有重复的数据进行排序(3)存储巧妙,节约空间,效率高
  • BitMap的缺点(1)数据不允许重复。因为只有0和1没有其他了(2)数据量少的时候相对于普通的hash没有优势(因为存储数据量小,我们直接用正常的map或者其他数据结构存储就行)
      • (3)无法处理字符串(4)无法解决Hash冲突(因此我们的前提条件就是数据不重复)

原文地址:https://www.cnblogs.com/leaveast/p/12052873.html

时间: 2024-10-29 02:51:47

<BitMap>大名鼎鼎的bitmap算法的相关文章

关于bitmap recycle trying to use a recycled bitmap android.graphics.Bitmap

在开发中,一直使用4.0以上手机作为测试机所以一直没有出现这个问题,今天换了2.3版本的手机,出现了这个错误: trying to use a recycled bitmap android.graphics.Bitmap 后检查代码,我的图片回收代码是介个样子的: public static void recycle(View view) { if (null == view) { return; } BitmapDrawable bd = (BitmapDrawable) view.getB

c# bitmap和new bitmap(bitmap)

问题情境: 给picturebox赋image属性,我用一下代码,出错: Bitmap theBitmap = convertCameraData.display(rawDataArray, height, width, rawImageArray, rgbPalette_256, backgroundTemperature); if (theBitmap == null) { return; } form_Main.pictureBox1.Image = theBitmap; 困境辨析: 把b

BitMap算法详解

所谓的BitMap就是用一个bit位来标记某个元素所对应的value,而key即是该元素,由于BitMap使用了bit位来存储数据,因此可以大大节省存储空间. 基本思想: 这此我用一个简单的例子来详细介绍BitMap算法的原理.假设我们要对0-7内的5个元素(4,7,2,5,3)进行排序(这里假设元素没有重复).我们可以使用BitMap算法达到排序目的.要表示8个数,我们需要8个byte. 1.首先我们开辟一个字节(8byte)的空间,将这些空间的所有的byte位都设置为0 2.然后便利这5个元

(转)根据ImageView的大小来压缩Bitmap,避免OOM

本文转载于:http://www.cnblogs.com/tianzhijiexian/p/4254110.html Bitmap是引起OOM的罪魁祸首之一,当我们从网络上下载图片的时候无法知道网络图片的准确大小,所以为了节约内存,一般会在服务器上缓存一个缩略图,提升下载速度.除此之外,我们还可以在本地显示图片前将图片进行压缩,使其完全符合imageview的大小,这样就不会浪费内存了. 一.思路 思路:计算出要显示bitmap的imageview大小,根据imageview的大小压缩bitma

Android性能优化:谈谈Bitmap的内存管理与优化

最近除了忙着项目开发上的事情,还有就是准备我的毕业论文,有一小段时间没写博客了,今晚难得想总结一下,刚好又有一点时间,于是凑合着来一篇,好了,唠叨话不多说,直接入正题.从事Android移动端的开发以来,想必是经常要与内存问题打交道的,说到Android开发中遇到的内存问题,像Bitmap这种吃内存的大户稍微处理不当就很容易造成OOM,当然,目前已经有很多知名的开源图片加载框架,例如:ImageLoader,Picasso等等,这些框架已经能够很好的解决了Bitmap造成的OOM问题,虽然这些框

Android Bitmap面面观

转自android程序员:http://mp.weixin.qq.com/s?__biz=MzA4MjU5NTY0NA==&mid=404530070&idx=1&sn=e2580b69d6ec73dabf8160216aa6702a&scene=23&srcid=#rd 开通赞赏后的第一篇文章,仍然由大家熟悉的冯建同学投稿,他的这几篇文章可以说风格类似,都是某一个专题的详细整理,这次轮到Bitmap了.点击这里可以快速访问他的上一篇投稿: 你应该知道的那些Andr

海量数据处理之BitMap

有这样一种场景:一台普通PC,2G内存,要求处理一个包含40亿个不重复并且没有排过序的无符号的int整数,给出一个整数,问如果快速地判断这个整数是否在文件40亿个数据当中? 问题思考: 40亿个int占(40亿*4)/1024/1024/1024 大概为14.9G左右,很明显内存只有2G,放不下,因此不可能将这40亿数据放到内存中计算.要快速的解决这个问题最好的方案就是将数据搁内存了,所以现在的问题就在如何在2G内存空间以内存储着40亿整数.一个int整数在java中是占4个字节的即要32bit

根据ImageView的大小来压缩Bitmap,避免OOM

Bitmap是引起OOM的罪魁祸首之一,当我们从网络上下载图片的时候无法知道网络图片的准确大小,所以为了节约内存,一般会在服务器上缓存一个缩略图,提升下载速度.除此之外,我们还可以在本地显示图片前将图片进行压缩,使其完全符合imageview的大小,这样就不会浪费内存了. 一.思路 思路:计算出要显示bitmap的imageview大小,根据imageview的大小压缩bitmap,最终让bitmap和imageview一样大. 二.获得ImageView的宽高 int android.view

C# byte数组转成Bitmap对象

方法一: /// <summary> /// 将数组转换成彩色图片 /// </summary> /// <param name="rawValues">图像的byte数组</param> /// <param name="width">图像的宽</param> /// <param name="height">图像的高</param> /// <