《编程珠玑》代码之路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代表箱数。

猜你喜欢

转载自blog.csdn.net/beijixiong5622/article/details/84979489