【读书笔记】《编程珠玑》第一章之位向量&位图

此书的叙述模式是借由一个具体问题来引出的一系列算法,数据结构等等方面的技巧性策略。共分三篇,基础,性能,应用。每篇涵盖数章,章内案例都非常切实棘手,解说也生动有趣。

自个呢也是头一次接触编程技巧类的书籍,而且算法数据结构方面的知识储备实在是薄弱,这么看来,纯粹找虐啊orz。今此行为,歇业养伤,实属无聊。也可说是自打毕业后,看书如打仗,自视身处“安安稳稳的和平年代”,闲来了也就闲着,忧患意识甚少,有也退退缩缩。话说回来,这本书不像CLRS那种难打的硬仗(现在想想都脑仁疼啊),《Programming Pearls》这么个好对手,排烦解闷也正好练练身手!

废话了一大堆,嘿嘿,看官轻拍。那下面就来看看第一章讲了些啥~

问题

怎样给一个最多包含1千万条记录且每条记录都是7位的整数的磁盘文件排序?

准确描述

这里书本对问题抽象出了精确的描述:

输入:一个最多包涵n个正整数的文件,每个数都小于n,其中n=10^7。如果在输入文件中有任何整数重复出现就是致命错误。没有其他数据与该整数相关联。

输出:按升序排列的输入整数的列表。

约束:最多有(大约)1MB的内存空间可用,有充足的磁盘存储空间可用。运行时间最多几分钟,运行时间为10秒就不需要进一步优化了。

解决方案

  1. 归并排序。联想下归并排序的缺点:需要O(n)的辅助空间,可知他必将多次的多写一块额外的工作文件or内存。优势:只需读取一次。劣势:需要数次操作额外文件。
  2. 分批读入数据,然后使用快速排序。每个整数用32位即4字节表示最多可表示带符号的2^31-1>9 999 999,计算10^7/(10^6/4)可得,40次读取排序可以达到目标。优势:快。劣势:多次读取数据,多趟算法。
  3. 使用位图数据结构。这个一个(10^7/32 + 1) 行 (32) 列的大表:
    31 30 ... 1 0
    31 30 ... 1 0
    ......
    31 30 ... 1 0
    31 30 ... 1 0

  如果数据是{2,3,5},那么第一行上就会是:0000 0000 0000 0000 0000 0000 0010 1100。很明显的,文件里所有的整数都可以在这张表里找到对应的位置,聪明如你,这就是一个大小为10^7/32 + 1的int[],具体的算法是这样的:

package chpt1;

import java.util.List;

/**
 * Created by wqi on 2016/10/22.
 */
public class BitSort  {
    // 位向量,也可称作位图,这里为了形象化,取作bitMap.
    private int[] bitMap;

    // i >> 5即每32个数为一组,也就是数组重的一个int元素。 i & 0x1f 相当于取该数对32取模,就找到了该数对应的位,|= 即为置1。
    private void set(int i) {
        this.bitMap[i >> 5] |= (1 << (i & 0x1f));
    }

    // 和set()本质上的区别就是 &= 将该数对应的位置0
    private void clr(int i) {
        this.bitMap[i >> 5] &= ~(1 << (i & 0x1f));
    }

    // 返回该数对应的位为1或为0
    private int test(int i) {
        return (this.bitMap[i >> 5] & (1 << (i & 0x1f)));
    }

    public void sort(List<Integer> list) {
        this.bitMap = new int[list.size() / 32 + 1];

        for (int i = 0; i < this.bitMap.length; i++) {
            clr(i);// 第一阶段将所有的位都置为0。
        }
        for (int i = 0; i < list.size(); i++) {
            set(list.get(i));// 第二阶段读入文件中的每个整数来建立集合,将每个对应的位都置为1。
        }
        for (int i = 0; i < list.size(); i++) {
            if (test(i) == 1){
                // 第三阶段检验每一位,如果该位为1,则输出这个数。
            }
        }
    }
}

优势:只需要读取一次文件数据,且不需要额外文件。而且使用的是基本的位操作,速度会更快。劣势:没有,这算法相当友好。

总结  

这一章在英文原版中名为 Cracking the Oyster ,直译为「美妙的(也可作开裂的)牡蛎」。和开篇概览中提到的『对实例研究的深入思考不仅很有趣,而且可以获得实际的益处』很贴切,牡蛎多鲜啊,pearls 的美也很直观的看到。再扯句题外话,貌似在西方国家 Oyster 这个词的意味是很好的,沙翁的台词中有 the world is my oyster 意译可表示成——我可以为所欲为啦。

位图这种数据抽象确实很有趣!巧妙的解决了这种看上去很乍乎的大数据问题。在网上也了解到,很多时候在处理大批量数据时,都可以考虑使用该数据结构,这点往后有待补充,希望在这再写几篇做到『举一隅,不以三隅反』。另外,针对位图这一数据概念,Java已经有操作起来非常简单的实现了,就是BitSet(),该集合类初始范围在0~63(JDK Version: 1.8.0_101),如果这时候BitSet().set(64),它会自动扩容至当前容量的翻倍,也就是*2,在这即0~127。

就先写到这,2016年10月22日16:56:01。课后习题部分是很依赖对前文的理解,且变化不是太大,书后答案代码部分大都是用C/C++写的,但只会Java的我看起来也不麻烦(看到qsort好羡慕C系的程序员啊),明天看看有没有有意思的题目再补充吧。#明日债明日还#

;-)

时间: 2024-08-09 21:49:48

【读书笔记】《编程珠玑》第一章之位向量&位图的相关文章

编程珠玑第一章习题

第一章习题1解析 以下代码均使用MSVC 6.0编译运行通过,为了便于学习,在原代码的基础上进行了一定的修改. 1.如何使用一个具有库的语言来实现一种排序算法以表示和排序集合,将代码实现可用 1 #include<stdio.h> 2 #include<stdlib.h> 3 int comp(const void *x,const void *y){ 4 return (*(int*)x-*(int*)y); 5 } 6 int a[5]={45,25,64,10,4}; 7 v

编程珠玑(续) 读书笔记 -(前言+第一章性能监视工具)

<ACM通讯> 一次一章,仔细地读 ANSI American National Standards Institute  美国国家标准学会 1.1计算素数 #include<stdio.h> int prime(int n ) { int i; for(i =2;i<n;i++){ 999 if(n%i==0) 78022 return 0; 831 return 1; 168 } } main() { int i ,n; n=1000; 1 for(i=2;i<=n

编程珠玑第一章习题答案

习题 1.1      如果不缺内存,如何使用一个具有库的语言来实现一种排序算法? 因为C++有sort,JAVA也有,这里以C++为例给出,记住如果用set集合来排序时,是不可以有元素重复的 代码: #include <iostream> #include <cstring> #include <cmath> #include <queue> #include <stack> #include <list> #include <

编程珠玑 第一章

题目:一个最多包含n个正整数的文件,每个数都小于n,其中n=10^7,且所有正整数都不重复.求如何将这n个正整数升序排列. 约束:最多有1MB的内存空间可用,有充足的磁盘存储空间. 习题2 习题3 实现位向量用于排序. #include<stdio.h> #define N 10000000 #define Shift 5 #define BitPerWord (sizeof(int)*8) #define Mask ((1<<Shift)-1) int a[1+N/BitPerW

[读书笔记]Effective Java 第一章

需要了解JAVA最近每个版本新增的特性,并善用这些新特性为自己的程序实现高效简洁的代码. 其中提到的编程原则包括: 模块要尽可能的小 代码应该要被重用,而不是被拷贝 模块之间的依赖性应该尽可能降到最小 错误应该尽早被检测,最好是在编译期

[读书笔记·编程珠玑] 数组移位(待填坑)

要求:已知一个一维数组 arr ,现在要求将它向左旋转n个位置. 方法一: 假设允许开辟一个临时空间,那么问题就变得简单了,可以开辟一个跟arr长度相同的空间,然后隔n个位置不断添加元素即可,思路比较简单,下面是代码实现: void RotateLeft1(vector<int> &arr, const int step) { vector<int> ret; int sz = arr.size(); ret.resize(sz); for (int i = 0; i &l

编程珠玑——第一章习题

1.如果不缺内存,如何使用一个具有库的语言来实现以后总排序算法和排序集合? 答:这个不同语言有不同的库函数排序C有qsort,java有sort排序,具体就不贴代码了.C++有实现排序的库函数:sort,该函数的实现是快速排序.另外C++的容器Map和set均可以实现排序.由于Map和set的实现是红黑树,所以具有自动排序功能. 2.如何使用位逻辑运算(如与.或.移位)来实现位向量? 这个道题的核心就在于想要把某bit置0,将该位直接和0做与操作,想要保持某bit位不变,将该位与1做与操作,想要

编程珠玑第一章习题6.1000个整数排序

题目描述: 1~1000万的整数,随机挑出1000个整数(可重复),每个整数最多可以出现10次.将这些整数按照升序排序. 分析:      1000个整数,我们可以用1000万个字符按大小来记录它出现的次数,如同 3, 1, 5, 6,5  用5个字符数组表示就是 { 1, 0, 1, 0, 2, 1}.1出现1次,所以第一个字符就用1记录:2出现0次,用0记录,以此类推. 然后,我们按从左往右的顺序,第 i 索引的字符是多少,就输出多少遍该索引(索引即是你要输出的整数).输出结果是:1,3,5

[读书笔记]计算机组成原理-第一章系统概论

1.1 计算机么有软硬件构成,软件就程序之类的,硬件就看得见的之类的,硬盘等. 计算机系统是多层次的.大概就是 M4(高级语言翻译机) ↓ M3(汇编翻译机) ↓ M2(操作系统把) ↓ M1(机器语言机,就是2进制的东西在这个上运行) ↓ M0(微机,把M1中的一条指令拿过来,解释执行,好了再下一条.如此反复.看成是对M1的分解把) M4翻译高级语言,如C,js这些为汇编再到机器语言,要么直接翻译为机器语言.M4翻译成机器语言的程序有2种,编译程序和解释程序,编译么一次编译好,再执行机器语言,