咱們先來看個簡單的問題。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內存了。
你們再想一個問題,雖然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的存儲會在以後的文章講哦
完
推薦閱讀:
獲取更多原創文章,能夠關注下個人公衆號:苦逼的碼農,我會不按期分享一些資源和軟件等。。同時也感謝把文章介紹給更多須要的人。