有一个看起来很简单的问题:如何存储一波随机整数,使得查找和存储效率尽可能高?
通常的办法自然是数组和链表,当然如果这么玩,那博客就没必要写了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代表箱数。