經典算法- BitMap
原文鏈接https://www.jianshu.com/p/6082a2f7df8e
一、問題引入
舉一個例子,有一個無序有界int數組{1,2,5,7},初步估計占用內存4X4=16字節,這倒是沒什么奇怪的,但是假如有10億個這樣的數呢,如果這樣的一個大的數據做查找和排序,那估計內存也崩潰了,有人說,這些數據可以不用一次性加載,那就是要存盤了,存盤必然消耗IO。我們提倡的是高性能,這個方案直接不考慮。
二、問題分析
bitmap: 基于位的映射
一個byte是占8個bit,如果每一個bit的值就是有或者沒有,也就是二進制的0或者1,如果用bit的位置代表數組值有還是沒有,那么0代表該數值沒有出現過,1代表該數組值出現過。不也能描述數據了嗎?具體如下圖:
這樣,一個占用32bit的數據現在只占用了1bit,節省了不少的空間,排序就更不用說了,一切顯得那么順利。這樣的數據之間沒有關聯性,要是讀取的,你可以用多線程的方式去讀取。時間復雜度方面也是O(Max/n),其中Max為byte[]數組的大小,n為線程大小。
處。
三、應用與代碼
基于bit運算實現
再看代碼之前,我們先搞清楚一個問題,一個數怎么快速定位它的索引號,也就是說搞清楚byte[index]的index是多少,position是哪一位。舉個例子吧,例如add(14)。14已經超出byte[0]的映射范圍,在byte[1]范圍之類。那么怎么快速定位它的索引呢。如果找到它的索引號,又怎么定位它的位置呢。Index(N)代表N的索引號,Position(N)代表N的所在的位置號。
Index(N) = N/8 = N >> 3;
Position(N) = N%8 = N & 0x07;
1. add(int num)
你要向bitmap里add數據該怎么辦呢,不用擔心,很簡單,也很神奇。
上面已經分析了,add的目的是為了將所在的位置從0變成1.其他位置不變.
(下圖應是作或運算 )
實例代碼
public void add(int num){
// num/8得到byte[]的index
int arrayIndex = num >> 3;
// num%8得到在byte[index]的位置
int position = num & 0x07;
//將1左移position后,那個位置自然就是1,然后和以前的數據做或運算,這樣,那個位置就替換成1了。
bits[arrayIndex] |= 1 << position;
}
2. clear(int num)
對1進行左移,然后取反,最后與byte[index]作與操作。
public void clear(int num){
// num/8得到byte[]的index
int arrayIndex = num >> 3;
// num%8得到在byte[index]的位置
int position = num & 0x07;
//將1左移position后,那個位置自然就是1,然后對取反,再與當前值做&,即可清除當前的位置了.
bits[arrayIndex] &= ~(1 << position);
}
3. contain(int num)
public boolean contain(int num){
// num/8得到byte[]的index
int arrayIndex = num >> 3;
// num%8得到在byte[index]的位置
int position = num & 0x07;
//將1左移position后,那個位置自然就是1,然后和以前的數據做&,判斷是否為0即可
return (bits[arrayIndex] & (1 << position)) !=0;
}
全部代碼
package com.chs.alg.bitmap;
public class BitMap {
//保存數據的
private byte[] bits;
//能夠存儲多少數據
private int capacity;
public BitMap(int capacity){
this.capacity = capacity;
//1bit能存儲8個數據,那么capacity數據需要多少個bit呢,capacity/8+1,右移3位相當于除以8
bits = new byte[(capacity >>3 )+1];
}
public void add(int num){
// num/8得到byte[]的index
int arrayIndex = num >> 3;
// num%8得到在byte[index]的位置
int position = num & 0x07;
//將1左移position后,那個位置自然就是1,然后和以前的數據做|,這樣,那個位置就替換成1了。
bits[arrayIndex] |= 1 << position;
}
public boolean contain(int num){
// num/8得到byte[]的index
int arrayIndex = num >> 3;
// num%8得到在byte[index]的位置
int position = num & 0x07;
//將1左移position后,那個位置自然就是1,然后和以前的數據做&,判斷是否為0即可
return (bits[arrayIndex] & (1 << position)) !=0;
}
public void clear(int num){
// num/8得到byte[]的index
int arrayIndex = num >> 3;
// num%8得到在byte[index]的位置
int position = num & 0x07;
//將1左移position后,那個位置自然就是1,然后對取反,再與當前值做&,即可清除當前的位置了.
bits[arrayIndex] &= ~(1 << position);
}
public static void main(String[] args) {
BitMap bitmap = new BitMap(100);
bitmap.add(7);
System.out.println("插入7成功");
boolean isexsit = bitmap.contain(7);
System.out.println("7是否存在:"+isexsit);
bitmap.clear(7);
isexsit = bitmap.contain(7);
System.out.println("7是否存在:"+isexsit);
}
}
四、bitmap排序實例
以下參考https://wizardforcel.gitbooks.io/the-art-of-programming-by-july/content/06.07.html
//定義每個Byte中有8個Bit位
#include <memory.h>
#define BYTESIZE 8
void SetBit(char *p, int posi)
{
for(int i=0; i < (posi/BYTESIZE); i++)
{
p++;
}
*p = *p|(0x01<<(posi%BYTESIZE));//將該Bit位賦值1
return;
}
void BitMapSortDemo()
{
//為了簡單起見,我們不考慮負數
int num[] = {3,5,2,10,6,12,8,14,9};
//BufferLen這個值是根據待排序的數據中最大值確定的
//待排序中的最大值是14,因此只需要2個Bytes(16個Bit)
//就可以了。
const int BufferLen = 2;
char *pBuffer = new char[BufferLen];
//要將所有的Bit位置為0,否則結果不可預知。
memset(pBuffer,0,BufferLen);
for(int i=0;i<9;i++)
{
//首先將相應Bit位上置為1
SetBit(pBuffer,num[i]);
}
//輸出排序結果
for(int i=0;i<BufferLen;i++)//每次處理一個字節(Byte)
{
for(int j=0;j<BYTESIZE;j++)//處理該字節中的每個Bit位
{
//判斷該位上是否是1,進行輸出,這里的判斷比較笨。
//首先得到該第j位的掩碼(0x01<<j),將內存區中的
//位和此掩碼作與操作。最后判斷掩碼是否和處理后的
//結果相同
if((*pBuffer&(0x01<<j)) == (0x01<<j))
{
printf("%d ",i*BYTESIZE + j);
}
}
pBuffer++;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
BitMapSortDemo();
return 0;
}
智能推薦
bitmap 算法的實現 與作用
問題引入 有一個無序有界int數組{1,2,5,7},初步估計占用內存44=16字節,因為只有4個數,很容易,可以很快找到需要的數。但是假如有10億個這樣的數呢,10億個不重復并且沒有排過序的無符號的int整數,給出一個整數,找出給定的某個數,你該如何操作? 需求分析:Int類型在Java中的存儲占用4個Byte,32Bit。10億4/(102410241024)=3.72G左右。如果這樣的一個大...
BitMap算法的說明以及驗證
核心 1、什么是BitMap算法 2、BitMap的優缺點 3、BitMap的實現 4、BitMap 的比對 1、什么是BitMap算法 在大數據時代,我們如果將一個上億的整數文件來做排序或者查詢使用,這個就將面臨一個問題,內存不夠用,例如1億個整數的文件放入內存將會占用380MB(100000000*4/1024/1024)空間.如果是10億或者更多呢,這個將對內存是一個巨大的挑戰。為了解決這個...
freemarker + ItextRender 根據模板生成PDF文件
1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...
電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!
Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...
猜你喜歡
requests實現全自動PPT模板
http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...
Linux C系統編程-線程互斥鎖(四)
互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...
統計學習方法 - 樸素貝葉斯
引入問題:一機器在良好狀態生產合格產品幾率是 90%,在故障狀態生產合格產品幾率是 30%,機器良好的概率是 75%。若一日第一件產品是合格品,那么此日機器良好的概率是多少。 貝葉斯模型 生成模型與判別模型 判別模型,即要判斷這個東西到底是哪一類,也就是要求y,那就用給定的x去預測。 生成模型,是要生成一個模型,那就是誰根據什么生成了模型,誰就是類別y,根據的內容就是x 以上述例子,判斷一個生產出...
styled-components —— React 中的 CSS 最佳實踐
https://zhuanlan.zhihu.com/p/29344146 Styled-components 是目前 React 樣式方案中最受關注的一種,它既具備了 css-in-js 的模塊化與參數化優點,又完全使用CSS的書寫習慣,不會引起額外的學習成本。本文是 styled-components 作者之一 Max Stoiber 所寫,首先總結了前端組件化樣式中的最佳實踐原則,然后在此基...