日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

教你用BitMap排序、查找和存储大量数据

發布時間:2025/3/20 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 教你用BitMap排序、查找和存储大量数据 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Bit-map的基本思想就是用一個bit位來標記某個元素對應的Value,而Key即是該元素。由于采用了Bit為單位來存儲數據,因此在存儲空間方面,可以大大節省。

Bit-map概述

假設現在有這樣一個需求:在20億個隨機整數中找出某個數是否存在其中,并假設32位操作系統,4G內存,你會怎么做?我們知道在java中,一個int占4字節,1字節=8位(1 byte = 8 bit),如果每個數字用int存儲,那就是20億個int,因而占用的空間約為:

2000000000*4/1024/1024/10247.45 G

顯然消耗的內存空間太多了。如果按位存儲就不一樣了,20億個數就是20億位,占用空間約為:

2000000000/8/1024/1024/10240.23 G

那么使用Bit-map到底是怎么來存儲的以及它的過程是什么樣子的呢?接下里帶大家一起研究一下。
在最開始的時候我們說過,Bit-map每一位表示一個數,0表示不存在,1表示存在。例如我們要存儲數字{1,3,4,6},則可以使用Bit-map存儲結構如下:

計算機內存分配的最小單位是字節,也就是8位,那如果要表示{12,13,15}怎么辦呢?當然是在另一個8位上表示了,如下圖所示:

大家有沒有感覺這樣得結構有點眼熟?不錯,好像變成一個二維數組。我們知道1個int占32位,如果我們要存儲得數字得最大數字是N,則我們只需要申請一個int數組長度為 int b[1+N/32] 即可存儲,其中:

b[0]:可以表示0~31; b[1]:可以表示32~63; b[2]:可以表示64~95; ...

這樣,給定任意整數N,那么N/32就得到下標,N%32就知道它在此下標的哪個位置。

Bit-map操作原理

以上簡單概括性的對Bit-map的原理進行了介紹,接下來對于常用的操作我們具體看一下是怎么對數據進行處理的。

添加數據

如果現在我們想把5這個數字放進去,怎么做呢?上面提到了,給定任意整數N,那么N/32就得到下標,N%32就知道它在此下標b的哪個位置。首先,5/32=0,也是說它應該在b[0],5%32=5,說明它應該在b[0]的第5個位置,那我們把1向左移動5位,然后與原數據按位或:

換成二進制就是:01001110|0010000=01101110,這就相當于 78 | 32 = 110,也就是說,要想插入一個數,將1左移代表該數字的那一位,然后與原數進行按位或操作,即:

b[0] = b[0] | (1<<5)

因此,公式可以概括為:p + (i/8)|(1<<(i%8)) 其中,p表示現在的值,i表示待插入的數。

清除數據

假設我們要移除某個數字N,該怎么做呢?只需將該數所在的位置為0即可,那么怎么可以做到呢?為了便于理解,我們還是將結構看成是一個二位數組。則步驟可以歸納如下:

  • N/32得到下標i,N%32得到此下標的位置index;
  • 將1左移index位,就到達index這個數字所代表的位;
  • 按位取反,最后與原數按位與,將改位置置0。
  • 假設我們要6移除,我們用圖示來表示以一下執行過程:

    查找數據

    前面我們也說了,每一位代表一個數字,1表示有(或者說存在),0表示無(或者說不存在)。通過把該為置為1或者0來達到添加和清除的效果,那么判斷一個數存不存在就是判斷該數所在的位是0還是1。所以查找相對來說比較簡單。假設,我們想知道6在不在,那么只需判斷 b[0] & (1<<6) 如果這個值是0,則不存在,如果是1,就表示存在。

    Bitmap用途

    快速排序

    假設我們要對0-7內的5個元素(4,7,2,5,3)排序(這里假設這些元素沒有重復),我們就可以采用Bit-map的方法來達到排序的目的,要表示8個數,我們就只需要8個Bit(1Bytes):

  • 首先我們開辟1Byte的空間,將這些空間的所有Bit位都置為0,然后將對應位置為1。
  • 遍歷一遍Bit區域,將該位是1的位的編號輸出(2,3,4,5,7),這樣就達到了排序的目的,時間復雜度O(n)。
  • 優點:

    • 運算效率高,不需要進行比較和移位;
    • 占用內存少,比如N=10000000,只需占用內存為N/8=1250000Byte=1.25M。

    缺點:

    • 所有的數據不能重復,即不可對重復的數據進行排序和查找;
    • 只有當數據比較密集時才有優勢

    快速去重

    假設讓你從20億個整數中找出不重復的整數的個數,提前是內存不足以容納這20億個整數,那么你會用什么辦法?使用Bit-map就可以很好的解決。關鍵的問題就是怎么設計Bit-map來表示這20億個數字的狀態了。

    一個數字的狀態只有三種,分別為不存在,只有一個,有重復。因此,我們只需要2bits就可以對一個數字的狀態進行存儲了,假設我們設定一個數字不存在為00,存在一次01,存在兩次及其以上為11,那我們大概需要存儲空間2G左右。所以可以這樣進行操作:

  • 把這20億個數字放進去(存儲),如果對應的狀態位為00,則將其變為01,表示存在一次;
  • 如果對應的狀態位為01,則將其變為11,表示已經有一個了,即出現多次;
  • 如果為11,則對應的狀態位保持不變,仍表示出現多次。
  • 最后,統計狀態位為01的個數,就得到了不重復的數字個數,時間復雜度為O(n)。
  • 快速查找

    int數組中的一個元素是4字節占32位,那么除以32就知道元素的下標,對32求余數(%32)就知道它在哪一位,如果該位是1,則表示存在。

    Bloom Filters

    Bloom Filters概述

    Bloom filter 是一個數據結構,它可以用來判斷某個元素是否在集合內,具有運行快速,內存占用小的特點。這個存儲的原理與Bit-map類似,所以我們在這里一并介紹一下。
    Bloom Filter 是一個基于概率的數據結構:它只能告訴我們一個元素絕對不在集合內或可能在集合內。如果想判斷一個元素是不是在一個集合里,一般想到的是將集合中所有元素保存起來,然后通過比較確定。鏈表、樹、散列表(哈希表)等等數據結構都是這種思路,但是隨著集合中元素的增加,需要的存儲空間越來越大;同時檢索速度也越來越慢,檢索時間復雜度分別是O(n)、O(log n)、O(1)。
    布隆過濾器的原理是:

    • 當一個元素被加入集合時,通過 K 個散列函數將這個元素映射成一個位數組(Bit array)中的 K 個點,把它們置為 1 。
    • 檢索時,只要看看這些點是不是都是1就知道元素是否在集合中;
    • 如果這些點有任何一個 0,則被檢元素一定不在;
    • 如果都是1,則被檢元素很可能在(之所以說“可能”是誤差的存在),也可能不在。

    Bloom Filters流程

  • 首先需要 k 個 hash 函數,每個函數可以把 key 散列成為 1 個整數;
  • 初始化時,需要一個長度為 n 比特的數組,每個比特位初始化為 0;
  • 某個 key 加入集合時,用 k 個 hash 函數計算出 k 個散列值,并把數組中對應的比特位置為 1;
  • 判斷某個 key 是否在集合時,用 k 個 hash 函數計算出 k 個散列值,并查詢數組中對應的比特位,如果所有的比特位都是1,認為在集合中;
  • 如果這些點有任何一個 0,則被檢元素一定不在。

  • 在實際的項目應用中,布隆過濾器經常會被用在一些大規模去重,但又允許有小概率誤差的場景中,例如說我們對一組爬蟲網頁地址的去重操作,或者統計某些大型網站每天的用戶訪問數量(需要對相同用戶的多次訪問進行去重),還可以解決緩存穿透問題等。

    實際上,關于bitmap和布隆過濾器這類工具在大型互聯網企業上已經受到了廣泛使用,例如說java里面提供了BitSet類,Redis也提供了相應的位圖類,Google里面的guava工具包中的BloomFilter也已經實現類布隆過濾器,所以在實際應用的時候只需要直接使用這些現有的組件即可,避免重復造輪子的情況發生。

    總結

    以上是生活随笔為你收集整理的教你用BitMap排序、查找和存储大量数据的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。