《程式設計珠璣》程式碼之路18:用點陣圖和“箱”更快更省空間地儲存和查詢數字
阿新 • • 發佈:2018-12-14
有一個看起來很簡單的問題:如何儲存一波隨機整數,使得查詢和儲存效率儘可能高?
通常的辦法自然是陣列和連結串列,當然如果這麼玩,那部落格就沒必要寫了2333333。
一個32位整數int正常來說有32位,每種語言都有所不同,如果只是儲存和查詢數字的話,其實這是非常浪費的,而且是幾十倍的浪費。
比如,如果用最低位表示陣列0是否存在,1代表存在,0代表不存在,次低位表示1是否存在。那麼,這時候就可以在不影響讀寫速度的條件下,足足省下32倍的空間。
關鍵程式碼:
report函式,用來輸出所有的數,insert用來插入一個數,要訪問一個數也只需要像report函式裡的if語句一樣,效率非常高。隨機訪問複雜度和陣列一樣都是O(1),num陣列代表儲存陣列。
void report(int ans[]){ int j = 0; for (int i = 0; i < maxn; ++i){ if (queryBit(num, i)){ ans[j++] = i; } } } void insert(int val){ if (queryBit(num, val)){ return; } setBit(num, val); nCount++; } int setBit(int array[], int num){ array[num >> SHIFT] |= (1 << (MASK & num)); return 0; } int clearBit(int array[], int num) { array[num >> SHIFT] &= ~(1 << (MASK & num)); return 0; } bool queryBit(int array[], int num) { return 1 & array[num >> SHIFT] >> (MASK & num); }
還有一種結合連結串列和位向量優點的操作,名為“箱”
具體是,有一個數組,陣列每個元素都是一個連結串列頭,假如有0-99範圍的隨機數,有4個“箱”代表連結串列陣列的頭,0-24的數鏈在第一個連結串列後面,25-49放在第二個連結串列後面,以此類推,切記插入的時候有序插入,雖然插入需要遍歷連結串列,但由於整數隨機分佈的原因,期望時間複雜度其實非常低。
假設有m個箱,其實這m個箱可以看做一種雜湊,每個箱中的整數都用一個有序連結串列表示,由於整數是均勻分佈的,所以每個連結串列的期望長度都是1。
程式碼就沒啥好寫的了23333,需要注意的是一個數對映到哪個連結串列直觀的對映方式容易溢位,所以給出一個安全的對映關係:
i = t / (1 + maxval / m)
i代表對映的結果,t代表要對映的數字,m代表箱數。