1. 程式人生 > >《程式設計珠璣》程式碼之路18:用點陣圖和“箱”更快更省空間地儲存和查詢數字

《程式設計珠璣》程式碼之路18:用點陣圖和“箱”更快更省空間地儲存和查詢數字

有一個看起來很簡單的問題:如何儲存一波隨機整數,使得查詢和儲存效率儘可能高?

通常的辦法自然是陣列和連結串列,當然如果這麼玩,那部落格就沒必要寫了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代表箱數。