【 算法與數據結構專場 】BitMap 算法介紹

咱們先來看個簡單的問題。java

假如給你 20 億個非負數的 int 型整數,而後再給你一個非負數的 int 型整數 t ,讓你判斷 t 是否存在於這 20 億數中,你會怎麼作呢?算法

有人可能會用一個 int 數組,而後把 20 億個數給存進去,而後再循環遍歷一下就能夠了。數組

想一下,這樣的話,時間複雜度是 O(n),所須要的內存空間bash

4 byte * 20 億,一共須要 80 億個字節,測試

大概須要 8GB 的內存空間,顯然有些計算機的內存一次是加載不了這麼這麼多的數據的。優化

初步優化

按照上面的作法,時間複雜度是 O(n),內存是 8GB,實際上咱們是能夠把時間複雜度下降到 O(1) 的。ui

例如咱們能夠這樣來存數據,把一個 int 非負整數 n 做爲數組下標,若是 n 存在,則對應的值爲 1,若是不存在,對應的值爲 0。例如數組 arr[n] = 1,表示n存在,arr[n] = 0表示 n 不存在。spa

那麼,咱們就能夠把 20 億個數做爲下標來存,以後直接判斷 arr[t] 的值,若是 arr[t] = 1,則表明存在,若是 arr[t] = 0,則表明不存在。這樣,咱們就能夠把時間複雜度下降到 O(1)。不過空間複雜度咱們並無下降。還稍微大了點。code

因爲 int 非負整數一共有 2^31 個,因此數組的大小須要 2^32 這麼大。cdn

這裏可能有人說也能夠用HashSet來存啊,時間複雜度也是近似O(1)。不過這裏須要說明的是,HashSet裏面存的必須是對象,也就是說須要把int包裝成Integer,顯然一個對象的話是更花銷內存的,須要對象頭啊什麼的.....

再次優化

你們想一個問題,對於一個數,實際上咱們只須要兩種狀態,就是這個數存在不存在這兩種可能。上面咱們用1表明存在,用0表明不存在。

也就是說,咱們是能夠不用int型的數組來存儲的,一個int型佔用4個字節,即32個二進制位,一共能夠表示40億多個狀態。用int型的來存兩個狀態,多浪費。

因此咱們能夠考慮用boolean型的來存的,boolean貌似就佔用一個字節(java中的boolena貌似是佔用一個字節)。而一個boolean有true和false兩種狀態,因此也是成立的。這樣子的話佔用的內存就是2GB的內存了。

這樣,就能夠下降到以前的四分之1內存了。

最終優化:bitmap

你們再想一個問題,雖然boolean是表示兩種狀態,可是boolean實際上佔用了8bit啊,按道理8bit是能夠表示128種狀態的。而被咱們拿來表示兩個狀態,是否也有點浪費了呢?

咱們都知道,一個二進制位,有0和1兩種狀態,因此說,其實咱們是能夠用一個二進制位來表明一個int型的數是否存在的。例如對於1,3,5,7這四個數,若是存在的話,則能夠這樣表示:

1表明這個數存在,0表明不存在。例如表中01010101表明1,3,5,7存在,0,2,4,6不存在。

那若是8,10,14也存在怎麼存呢?如圖,8,10,14咱們能夠存在第二個字節裏

以此類推。這樣子,咱們又能夠把內存下降到以前的8分之一了。

這種採用一個二進制位來存儲數據的方法,咱們也叫作bitmap算法。

可能有人會問,假如我要添加一個數n,我知道它要存在第n個位那裏,把第n個二進制改成1,但是我要怎麼操做呢?

這個對於bitmap算法是如何存儲的,如何進行增刪操做的,我會在以後的文章裏講,這篇就大概介紹下bitmap算法。

Java中有自帶的bitmap實現,今天咱們就用Java中自帶的bitmap來作道題練練手。咱們換道相似題目吧,不知道你一眼是否就能想到用bitmap算法來作。

題目描述:

如今有五十億個int類型的正整數,要從中找出重複的數並返回。

判斷50億個數有哪些是重複和剛纔上面那個判斷是否存在,實際上是同樣的。咱們採用bitmap算法來作。不過這裏50億個數,別人確定是以文件流的形式給你的。這樣咱們爲了方便,咱們就假設這些數是以存在int型數組的形式給咱們的

代碼以下:

public class Test {
    //爲了方便,假設數據是以數組的形式給咱們的
    public static Set<Integer> test(int[] arr) {
        int j = 0;
        //用來把重複的數返回,存在Set裏,這樣避免返回重複的數。
        Set<Integer> output = new HashSet<>();
        BitSet bitSet = new BitSet(Integer.MAX_VALUE);
        int i = 0;
        while (i < arr.length) {
            int value = arr[i];
            //判斷該數是否存在bitSet裏
            if (bitSet.get(value)) {
                output.add(value);
            } else {
                bitSet.set(value, true);
            }
            i++;
        }
        return output;
    }
    //測試
    public static void main(String[] args) {
        int[] t = {1,2,3,4,5,6,7,8,3,4};
        Set<Integer> t2 = test(t);
        System.out.println(t2);
    }
}

複製代碼

打印結果:

[3, 4]
複製代碼

固然,bitmap算法的應用不單單是節省內存,它還有不少其餘的優勢。以後有機會就拿一些其餘的應用來寫篇文章。

本次講解到此結束。若是喜歡,能夠分享給更多的小夥伴哦。

bitmap的存儲會在以後的文章講哦

推薦閱讀:

獲取更多原創文章,能夠關注下個人公衆號:苦逼的碼農,我會不按期分享一些資源和軟件等。。同時也感謝把文章介紹給更多須要的人。

相關文章
相關標籤/搜索