辉爷的高级数据结构开讲了,辉爷V5啊,今天辉爷讲的是位图排序。
问题描述:
•输入
–给出至多10,000,000个正整数的序列
•特征:
–每个数都小于10,000,000
–数据不重复且 数据之间不存在关联关系
•输出:增序输出序列
•约束:
–内存容量1MB
–磁盘空间充足
–运行时间至多几分钟---最好线性时间
解答:
首先回顾下计数排序:
•思想:
–根据待排序中最大的数,开辟一个数组,记录每个整数出现的次数
–遍历一遍待排序的数据(排序的过程)
–顺序输出结果
•要求
–必须提前知道待排序数的最大数
•缺点
–有些数没有出现过,仍要为其保留一个空间
–只能针对整数进行处理
•优点
线性的排序算法
位图排序:
•引入位图排序的原因
–对计数排序算法进行空间压缩
•思想
–采用一个二进制位对应一个整数
•前提
–要知道待排序数的最大数
•存储
•使用字符或整形都可以
具体思路:
•举例
–存储:位图使用字符数组存储
•unsigned char bit[2];
•可用字节有两个0、1字节,一个字节中有八位
•我们只能表示16个数(0~15)
–问题
•给出一个数,怎么判断其对应位图的位置
–步骤
•找到该数对应的字节 +再找到该数对应的位
–方法
•14对应的字节:14/8 = 1
•14对应的位:第14%8位
•14是存储在第1个字节上的第6号位
•8是存储在第1个字节上的第0号位
•
已知:我们已找到待处理数所在的字节和位
•问题:
怎么设置对应位为1或0,且不能影响其他位
•思路:对其所在字节整体进行&和 |操作
•置1:
–1、先找到其所在字节:bitmap[num>>3]
–2、要把num对应位设为1即可
•难点:
–只把对应位为1,同时不影响该字节中的其他位
•方法:
–我们可以利用一个新的字节
–把该字节上num对应的位设为1,其他全为0
–之后与其所在字节进行或操作
1<<(num & 0b111);// 1向左移动多少位
置1、置0的思想
•置1:
–让其对应的二进制位为1,其他为0,与其对应字节,进行或操作
•置0:
–让其对应的二进制位为0,其他为1,与其对应的字节,进行与操作
优化
•优化的原因
–我们需要10,000,000个数表示10,000,000个位
–1MB的包含8*1024*1024个位
–所需内存容量:10,000,000/(8*1024*1024) = 1.20MB
•假如严格限制为1MB,可以采用的策略:
–两次遍历待排序列
•两次遍历待排序序列
–思想:把数据分成两部分,分别执行位图排序
–具体思路:
•第一次对1- 5,000,000之间的数排序
•之后再对5,000,001 -10,000,000之间的数排序
–需要存储空间为:5,000,000/(8*1024*1024) = 0.596MB
–总体上消耗的空间为0.596MB。
位图排序使用范围及应用
•位图排序使用范围
–(1)非负整数
–(2)每个整数最多出现一次
–(3)最大整数小于N
–(4)整数之间相互独立
•相关应用
–位图法存数据
–使用位图法判断数是否存在重复
–给40亿个不重复的unsignedint的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中
在STL中,bitset实现了相同功能
在STL中实现:
#include <bitset>
#include <iostream>
using namespace std;
int main()
{
const int numcount = 10;
const int MAXNUM = 20;
int arr[numcount] = {2,20,13,7,2,18,16,5,1,0};
bitset<MAXNUM+1> bitmap;
//指定为置1
for (int i=0; i<numcount; i++)
{
bitmap.set(arr[i]);
}
//输出排序结果;
for (int i=0; i<MAXNUM; i++)
{
if (bitmap.test(i))
{
cout<<i<<" ";
}
}
}
再感叹一句,辉爷V5啊~ 辉爷blog啊