在《编程珠玑》一书里提到了一种算法导论里没有提到过的位图排序方法,这种排序方法是通过牺牲空间效率来追求时间效率(线性时间)以达到时间-空间折中与双赢的目的。本文以实例形式简单讲一下位图排序思想。
一、问题描述
1.输入:一个至多包含1千万个非负整数的文件
2.特征:①每个数都是小于10000000的非负整数;②没有重复的数字;③数据之间不存在关联关系。
3.约束:①最多1MB的内存空间可用;②磁盘空间充足;③运行时间最多几分钟,最好是线性时间。
4.输出:按升序排列的整数序列。
二、位图排序思想
由于待排序的数据记录较多,我们单纯地使用常见的排序方法时间效率较低,运行时间会很长。而且内存空间有限(限制为1MB左右),所以我们不能同时把所有整数读入内存(如果每个整数使用7个字节来存储,那么1MB内存空间只能存大约143000个数字)。当然我们可以多次读取输入文件,多次排序,但是更好的方案是使用位图排序,可以使用有限的1MB内存空间并只进行一趟排序。
1.根据待排序集合中最大的数,开辟一个位数组,用来表示待排序集合中的整数;
2.待排序集合中的数字在位数组中的对应位置置1,其他的置0;
例如,待排序集合{1,2,3,5,8,13}可以表示为:0-1-1-1-0-1-0-0-1-0-0-0-0-1
这样排序过程自然可以分为三步:
第一步:将所有的位都置为0;
第二步:通过读入文件中的每个整数,将每个对应的位都置为1;
第三步:检验每一位,如果该位为1,输出对应的整数。
注意:位图排序是使用一个二进制位而不是一个整数来表示0或1,这样可以大大地减少所需要的内存空间。使用位图排序的前提是要知道待排序序列中的最大数。位图排序的缺点是有些数没有出现过,仍要为其保留一个位。故位图排序比较适合关键字密集的序列,例如一个城市的电话号码。
伪代码如下:
/*Phase 1: initialize set to empty*/ for i = [0, n) bit[i] = 0 /*Phase 2: insert present elements into the set*/ for each i in the input file bit[i] = 1 /*Phase 3: write sorted output*/ for i = [0, n) if bit[i] == 1 write i on the output file
性能:时间复杂度可达O(n),1MB包含8*1024*1024个位,所需内存10000000/(8*1024*1024)=1.20MB,如果不是严格限制的话可以看做基本符合要求。
三、位图排序实现
位图排序时,我们需要考虑:给出一个数,如何找到其对应位图的位置,方法就是首先找到该数对应的字节,然后在找到该数对应的位。例如:
unsigned char bitmap[2]; /* 可以表示16个数,即0~15 */
一个字节有八位,5表示第0个字节的第5位上;14表示第1个字节的第6个位上。
在这里为了简化位处理,我们使用C++标准库的bitset容器。bitset是C++提供的一种位集合的数据结构,它让我们可以像使用数组一样使用位,可以访问指定下标的bit位。和其他容器一样,bitset也是一个模板类。具体的bitset方法可以查看std::bitset reference。
下面我们使用bitset容器进行位图排序:
/************************************************************************* > File Name: BitSort.cpp > Author: SongLee ************************************************************************/ #include<bitset> #include<iostream> using namespace std; #define MAX 20 int main() { int arr[10] = {5,1,2,13,7,10,0,20,16,9}; bitset<MAX+1> bit; /* 将对应位置置1 */ for(int i=0; i<10; ++i) { bit.set(arr[i]); /* bit.set(n)表示将第n位置1 */ } /* 输出排序结果 */ for(int i=0; i<MAX+1; ++i) { /* bit.test(n)判断第n位是否为1 */ if(bit.test(i)) { cout << i << " "; } } cout << endl; }
输出结果:0 1 2 5 7 9 10 13 16 20
本文向大家介绍C++实现选择排序(selectionSort),包括了C++实现选择排序(selectionSort)的使用技巧和注意事项,需要的朋友参考一下 本文实例为大家分享了C++实现选择排序的具体代码,供大家参考,具体内容如下 一、思路 每次取剩下没排序的数中的最小数,然后,填到对应位置。(可以使用a[0]位置作为暂存单元) 如下: 二、实现程序 测试数据: 7 20 12 50 70 2
本文向大家介绍C++ 基数排序的实现实例代码,包括了C++ 基数排序的实现实例代码的使用技巧和注意事项,需要的朋友参考一下 C++ 基数排序 大家好,今天带来的是自己实现的用C++完成基数排序.在数据结构,算法分析和程序设计的学习过程中,我们经常也无法避免的要学到排序的算法.排序算法是程序设计过程中使用频率极高的算法之一,其输入是一组无序的序列,要求以升序或者降序的方式输出一组有序的序列.对于如
本文向大家介绍C++实现选择性排序(SelectionSort),包括了C++实现选择性排序(SelectionSort)的使用技巧和注意事项,需要的朋友参考一下 “选择性排序”是数列排序的算法之一。 其思路引点来源于经典的“可乐雪碧问题” “现有两杯饮料,一杯是雪碧,一杯是可乐,试问如何可以将两杯饮料交换?” “答:最简单的解决方案就是利用一个空杯,创造一个缓存区。” 选择性排序就是利用线性搜索
本文向大家介绍C#通过IComparable实现ListT.sort()排序,包括了C#通过IComparable实现ListT.sort()排序的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了C#通过IComparable实现ListT.sort()排序的方法,分享给大家供大家参考之用。具体方法如下: 通常来说,List<T>.sort()可以实现对T的排序,比如List<int>.so
本文向大家介绍C语言实现堆排序的简单实例,包括了C语言实现堆排序的简单实例的使用技巧和注意事项,需要的朋友参考一下 本文通过一个C语言实现堆排序的简单实例,帮助大家抛开复杂的概念,更好的理解堆排序。 实例代码如下:
本文向大家介绍C/C++实现八大排序算法汇总,包括了C/C++实现八大排序算法汇总的使用技巧和注意事项,需要的朋友参考一下 概述排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。 我们这里说说八大排序就是内部排序。 当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序。