位图和TOP(K)解法

之前在面试的被问到过10亿QQ号的排序和去重,当时没有考虑过内存的问题,直接就说快排然后去重。后来也想到了hash,但是仍然没有考虑过内存问题。所以在此总结一下此类问题。

位图

位图就是每一位都用来存放状态,适合于大规模数据,因为二进制为0,1,所以有两种状态,存在和不存在的判断。位图其实就是一个数组,只不过每一位都代表了另一个数,也可以看做是直接定址的哈希。
此图取自:https://blog.csdn.net/etalien_/article/details/90752420
我们将大数先除以32,划分到新的小单元里,然后每个单元里有32个位置,再模32找到自己在单元里位置。这样就把这个数存储到了一个二进制位。原本一个int型智能存储一个int数据,现在可以存储32个。若原本为16G,则处理后为16G/32=500M左右,大大减小了内存。
这样就需要有比特位置1操作,

void set(size_t x)
{
	size_t index=x>>5;//等同于x/32,查询在哪个单元
	size_t num=x%32;//单元上的哪个位置
	bitG[index]|=1<<num;//对应位置置1
}

还需i置0,与上面操作类似:

void reset(size_t x)
{
	size_t index=x>>5;//查询在哪个单元
	size_t num=x%32;//单元上的哪个位置
	bitG[index]&=(~(1<<num));//对应位置置0
}

在操作之前先判断此位置状态

bool test(size_t x)
{
	size_t index=x>>5;//查询在哪个单元
	size_t num=x%32;//单元上的哪个位置
	return bitG[index]&(1<<num);//已有此数返回1,没有返回0
}

完整代码如下:

class Bitnum{
	Bitnum(size_t range)
	{
		bitG.resize((range>>5)+1);//+1防止有数据泄露
	}
	void set(size_t x)
	{
		size_t index=x>>5;
		size_t num=x%32;
		bitG[index]|=1<<num;
	}
	void reset(size_t x)
	{
		size_t index=x>>5;
		size_t num=x%32;
		bitG[index]&=(~(1<<num));
	}
	bool test(size_t x)
	{
		size_t index=x>>5;
		size_t num=x%32;
		return bitG[index]&(1<<num);
	}
private:
	vector<int> bitG;
}

这就是位图的基本知识,没有很复杂。海量数据通过这样的方法进行排序和去重。

TOP(K)

这也是经常被问到的一个问题,还是上面的情景,10亿数据里,找出最大的K个数。
其实这就是一个排序问题,所以还是排序算法。
第一种:直接排序,然后区前K个。但是在数据很大的时候,内存无法容纳的时候,就不能用了。
第二种:快排,快排是有靶标的,第一次快排后,比靶标小的在前面,大的在后面。如果此时index小于K,那么K大在后面,快排index+1-end,如果index大于K,那么将左边快排,直到index==K。我们只关注一半的元素。但是也有数据多少限制。
第三种:最小堆法。我们建立一个K个元素的最小堆,然后遍历数据,当比堆顶小时,继续下一个,当大时,删除堆顶,插入此元素,重新调整最小堆。直到遍历完,最小堆就是结果。这只需要维持一个K的最小堆即可。所以可以处理多数据。
第四种:分治法。将全部数据划分为n份,也就是将数据拆分成可以处理的大小。再在每个里面找出K大的数。然后N*K继续比较,如果还是大则继续拆分。
第五种:哈希。哈希法去重,处理后能进入内存就可以用之前的方法。

内存问题

其实在实际处理数据时,经常出现内存不够问题,这时候我们就需要考虑问题的拆解。还有大数问题,这种需要突破硬件限制的问题,都需要纳入考虑范围。比如上面的拆分法,大数转字符显示等,都是灵活解决问题的办法。

上面的位图说到底还是只能处理数据问题,那要是字符串呢,怎么处理,我记得之前看过一个短视频的瀑布流介绍,里面有个很厉害的东西,布隆过滤器。哈哈哈,作为英雄联盟玩家第一反应是那个壮汉辅助。所以,之后会继续研究一下布隆过滤器,感兴趣可以继续看我之后的总结。

发布了22 篇原创文章 · 获赞 0 · 访问量 357

猜你喜欢

转载自blog.csdn.net/yanchenzhi/article/details/105045226
今日推荐