位图排序

《编程珠玑》第一章第一题就相当的精彩,做个笔记。题目如下:
输入:   一个包含n个正整数的文件,每个正整数小于n,n等于10的7次方(一千万)。并且文件内的正整数没有重复和关联数据。

输出:  输入整数的升序排列
 
约束: 限制在1M左右内存,充足的磁盘空间

    假设整数占32位,1M内存可以存储大概250000个整数,第一个方法就是采用基于磁盘的合并排序算法,第二个办法就是将0-9999999切割成40个区间,分40次扫描(10000000/250000),每次读入250000个在一个区间的整数,并在内存中使用快速排序。书中提出的第三个解决办法是采用bitmap(或者称为bit vector)来表示所有数据集合(注意到条件,数据没有重复),这样就可以一次性将数据读入内存,减少了扫描次数。算法的伪代码如下:
阶段1:初始化一个空集合
     for i=[0,n)
           bit[i]=0;
阶段2:读入数据i,并设置bit[i]=1
    for each i in the input file
           bit[i]=1;
阶段3:输出排序的结果
   for i=[0,n)
          if bit[i]==1
              write i on the output file

这个算法的时间复杂度在O(n),用c语言写的版本可以在10秒内完成任务!c语言的源码在该书主页上有,这里给一个java的测试版,加上我的理解注释:

/**
 * Created by IntelliJ IDEA.
 * User: zhuangxd
 * Date: 2008-1-7
 * Time: 14:30:44
 */
public class BitSortTest {
    private static final int BITSPERWORD = 32;  //整数位数
    private static final int SHIFT = 5;
    private static final int MASK = 0x1F;  //5位遮蔽 0B11111
    private static final int N = 10000000;
    //用int数组来模拟位数组,总计(1 + N / BITSPERWORD)*BITSPERWORD位,足以容纳N
    private static int[] a = new int[(1 + N / BITSPERWORD)];

    public static void main(String[] args) {
        bitsort(new int[]{1, 100, 2, 10000, 9999, 4567, 78902});
    }

    public static void bitsort(int[] array) {
        for (int i = 0; i < N; i++)
            clr(i);   //位数组所有位清0
        for (int i = 0; i < array.length; i++)
            set(array[i]);   //阶段2
        for (int i = 0; i < N; i++)
            if (test(i))
                System.out.println(i);
    }

    //置a[i>>SHIFT]的第(i & MASK)位为1,也就是位数组的第i位为1
    public static void set(int i) {
        a[i >> SHIFT] |= (1 << (i & MASK));
    }

    //置a[i>>SHIFT]的第(i & MASK)位为0,也就是位数组的第i位为0
    public static void clr(int i) {
        a[i >> SHIFT] &= ~(1 << (i & MASK));
    }

    //测试位数组的第i位是否为1
    public static boolean test(int i) {
        return (a[i >> SHIFT] & (1 << (i & MASK))) == (1 << (i & MASK));
    }
}

文章转自庄周梦蝶  ,原文发布时间2008-01-07

时间: 2024-11-03 00:37:09

位图排序的相关文章

【算法思想】位图排序算法

问题的提出 一个最多包含n个正整数的文件,每个数都小于n,其中n=10^7.假设最多只有1M的内存空间可用,在考虑空间和时间的优化的情况下,请问如何对其进行排序? 常规思想 我们假设这些整数都是用整型存储(一般整型的大小为4个字节),那么1M字节可以存储250 000个数据.由于输入文件最大可能有10^7个数据,因此可以通过遍历输入文件40次来完成排序.第一次将在[0,249 999]范围内的整数读入到内存中,第二次将在[250 000,499 999]范围内的整数读入到内存中,依此类推.每读入

用位图排序无重复数据集实例代码(C++版)_C 语言

<Programming Pearls>(编程珠玑下载)第一章讲述了如何用位图排序无重复的数据集,整个思想很简洁,今天实践了下. 一.主要思想 位图排序的思想就是在内存中申请一块连续的空间作为位图,初始时将位图的每一位都置为0,然后依次读取待排序文件的整数,将整数所在的位设置为1,最后扫描位图,如果某一位为1,则说明这个数存在,输出到已排序文件.比如待排序的数据S={3,0,4,1,7,2,5},max(S)=7,我们可以设置一个八位的位图B,将位图的每一位初始为0,即B=[0,0,0,0,0

PHP实现bitmap位图排序与求交集的方法_php技巧

本文实例讲述了PHP实现bitmap位图排序求交集的方法.分享给大家供大家参考,具体如下: 初始化一串全为0的二进制; 现有一串无序的整数数组; 如果整数x在这个整数数组当中,就将二进制串的第x位置为1; 然后顺序读取这个二进制串,并将为1的位转换成整数,顺序存放到新的集合中,就是排好序的了 排序代码: function sort() { // var_dump(PHP_INT_MAX, PHP_INT_SIZE); // int 9223372036854775807 // int 8 $bi

C++实现位图排序实例_C 语言

在<编程珠玑>一书里提到了一种算法导论里没有提到过的位图排序方法,这种排序方法是通过牺牲空间效率来追求时间效率(线性时间)以达到时间-空间折中与双赢的目的.本文以实例形式简单讲一下位图排序思想. 一.问题描述      1.输入:一个至多包含1千万个非负整数的文件      2.特征:①每个数都是小于10000000的非负整数:②没有重复的数字:③数据之间不存在关联关系.      3.约束:①最多1MB的内存空间可用:②磁盘空间充足:③运行时间最多几分钟,最好是线性时间.          

磁盘文件排序

磁盘文件排序 问题描述,来自<编程珠玑>: 输入:一个最多含有n个不相同的正整数的文件,其中每个数都小于等于n,且n=10^7. 输出:得到按从小到大升序排列的包含所有输入的整数的列表. 条件:最多有大约1MB的内存空间可用,但磁盘空间足够.且要求运行时间在5分钟以下,10秒为最佳结果. 分析: 1.归并排序.你可能会想到把磁盘文件进行归并排序,但题目要求中,你只有1MB的内存空间可用,所以,归并排序这个方法不行. 2.位图方案.例如正如<编程珠玑>一书上所述,用一个20位长的位字

编程珠玑之生成0至n-1之间的k个不同随机序列的扩展问题 --2014人人网笔试题目

  <编程珠玑>中习题1.4的题目是:"如果认真考虑了习题3,你将会面对生成小于n且没有重复的k个整数的问题.最简单的方法就是使用前k个正整数.这个极端的数据集合将不会明显的改变位图方法的运行时间,但是可能会歪曲系统排序的运行时间.如何生成位于0至n - 1之间的k个不同的随机顺序的随机整数?尽量使你的程序简短高效."  解决这个问题可以使用以空间换时间的方式,基本的思想是 利用洗牌的原理,将n个数(0至n-1)按次序排好,依次让每个数和一个随机挑选出的位子进行互换,这样肯

编写SQL需要注意的细节Checklist总结

复制代码 代码如下: /* --注意:准备数据(可略过,非常耗时) CREATE TABLE CHECK1_T1 ( ID INT, C1 CHAR(8000) ) CREATE TABLE CHECK1_T2 ( ID INT, C1 CHAR(8000) ) DECLARE @I INT SET @I=1 WHILE @I<=10000 BEGIN INSERT INTO CHECK1_T1 SELECT @I,'C1' INSERT INTO CHECK1_T2 SELECT 10000+

SQL细节之Checklist注意事项与总结

 代码如下 复制代码 /* --注意:准备数据(可略过,非常耗时) CREATE TABLE CHECK1_T1 (     ID INT,     C1 CHAR(8000) ) CREATE TABLE CHECK1_T2 (     ID INT,     C1 CHAR(8000) ) DECLARE @I INT SET @I=1 WHILE @I<=10000  BEGIN     INSERT INTO CHECK1_T1 SELECT @I,'C1'     INSERT INT

编写SQL需要注意的细节Checklist总结_MsSql

复制代码 代码如下: /* --注意:准备数据(可略过,非常耗时) CREATE TABLE CHECK1_T1 ( ID INT, C1 CHAR(8000) ) CREATE TABLE CHECK1_T2 ( ID INT, C1 CHAR(8000) ) DECLARE @I INT SET @I=1 WHILE @I<=10000 BEGIN INSERT INTO CHECK1_T1 SELECT @I,'C1' INSERT INTO CHECK1_T2 SELECT 10000+