浅显易懂的位图排序及C++实现

今天看到了一个博客,上面讲的是编程珠玑里面的一个问题,一种巧妙的排序方法——位图排序
问题描述如下:
输入:一个至多包含n个非负整数的文件,每个数都小于n; 且这些整数都不重复;数据之间也不存在关联关系。 此处n取值10000000。

约束:1,最多1MB的内存空间可用;2,磁盘空间充足;3,运行时间最多几分钟, 最好是线性时间。

输出:按升序排列的整数序列。
位图排序思想:

针对该题目的限制,有待排的数据记录太多(10000000),如果使用常规的排序方法,效率肯定会很低,而且内存空间有限(限制为1M),不能一次性将数据读取到内存当中,需要从文件中多次读入,编程珠玑中引出了一种新的排序方法,叫位图排序法。

所谓位图法,就是使用一串二进制串来表示待排序列元素集合的方法。比如,我们知道一个byte = 8bit,表示成二进制串就是:00000000,我们可以用bit的位置(对于一个字节就是0-7)来表示待序序列中是否出现一个数,1表示出现,0表示不出现。比如说00000010则表示待排序列包含1,因为bit 1为1。如果还不明白,继续看下面的两个例子和示图。 例如:待排序集合{3,6,0,2},由于这个序列的最大数是6,因此用一个byte就够了,因此可以分配一个只含一个元素的char数组,如char arr[1],然后看待排序列第一个元素是3,因此将数组元素的第3bit设为1,依次扫描直到待排序列的最后一个元素2,其位图可以用下图的左边表示。 那么如果待排序列的最大数不能用一个byte的最大位置表示了,比如说{3,6,0,2,8,9,10,12}了。很简单,那么就用2个字节的char数组,char arr[2],比如说待排序列中出现8,那么就将arr数组第二个元素的第0位设为1就可以了,依次类推,如下图右边所示。


好了,现在已经根据待排序列分配好char arr[ ]数组了,也根据待排序列的数据将数组arr的相应位设置好了。最后我们要做的很简单,就是从低到高位依次扫描这个数组的每一位,如果该位为1,表示这个数在待排序列中出现过,那么打印出或者写入到输出文件中,扫描完也就得到了排序后的序列了。

算法伪代码:
//第一步 初始化位置
for i = [0,n]
	bit[i]=0
//第二步 依次读入文件中的每一个数,将对应的位置 置为1
for each i in the input file
	bit[i] = 1
//第三步 校验每一位,如果该位为1,则输出对应的整数
for i = [0,n]
	if bit[i]==1
		output i on the output file
//end 
算法代码:
void bitmapsotr(void) //位图排序
{
	int a[] = { 9,3,1,0,7,8,2,4 };
	std::bitset<bitmapMaxLength + 1> bit;

	int size = sizeof(a) / sizeof(a[0]);

	for (int i = 0; i < size; i++)
	{
		bit.set(a[i]);  //bit.set(n)表示将第n位置1
	}

	std::cout << "After sort:" << std::endl;

	for (int i = 0; i < bitmapMaxLength; i++)
	{
		/*bit.test(n)判断第n位是否是1*/
		if (bit.test(i))
		{
			std::cout << i << "  ";
		}
	}
	std::cout <<std::endl;
}

这种排序方法是有限制:
1:需要知道待排序序列中的最大数,因为位图排序时需根据该值来分配数组空间。

2:位图排序比较适合元素密集的序列,因为在位图排序中,有些数没有出现过,仍要为其保留一个位。如果元素稀疏,则空间浪费很多,比如说一个待排序列{1,10000000,2},总共三个元素,却要保留10000000/8个字节,是不是有点太得不偿失了。3:待排序的元素不能重复。

转自博客:原文地址

猜你喜欢

转载自blog.csdn.net/u012183487/article/details/80437892