《编程珠玑》阅读小记(1)— 开篇

1. 前言

久闻《编程珠玑》一书的大名,一直没有找到合适的机会深入学习阅读,最近终于得以入手,便决心投入细细的研究,提升一下自己的编程思想与技术。阅读之后才发现,这本书确实一本不可多得的好书。它以计算机领域应用与编程算法相结合,让读者面对实际问题时,不单单局限于考虑该问题的解决方案,而是在入手实践之前能够驻足于考虑,该方案是否符合当前的实际环境,它的时间与空间的消耗是否达到了一个比较好的指标。

通过阅读这本书,很大程度上拓宽了我这样一个菜鸟程序员的视野。对于我来讲,发现要想真正的对书中内容有所理解、领悟,每一个小节我都不得不反复阅读两遍以上,唉,或许是我太笨了,正所谓“书读百遍,其义自见”,笨人有笨办法,就这样慢慢学习吧~

2. 开篇导读

本书第一章“开篇”,当我按照往日阅读速度完成第一章节时,却发现几乎不知所云。于是,只好回头重读!

2.1 问题描述

本章以一个常见磁盘文件排序开始介绍,这并不仅仅是一个简单的排序问题。我们要考虑的包括时间和空间的消耗。整个问题可以描述如下:

输入:一个最多包含n个正整数的文件,每个数都小于10^7,没有重复数据。n<=10000000;

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

约束条件:最多有1MB的内存空间可用,有充足的磁盘空间,运行时间最多几分钟,性能到达10秒钟之内无需进一步优化。


2.2 问题分析

对以上问题进行分析,引出归并排序与快速排序的对比:

(1)以一种基于磁盘的归并排序为起点,进行相应的调整,改变为针对当下问题的1000万条整数记录排序,代码量可由约200h缩减为几十行,但是归并排序的性能却不尽如人意,时间复杂度为T(n)=O(nlogn),完全执行该程序仍然需要几天时间。

也就是说,合并排序从输入中一次性读取文件,多次调用工作文件,之后对输出文件一次性写入完成整个过程。

(2)另一种解决方案,则考虑在1MB的内存空间中,多趟快排来实现。使用32位存储每个整数,那么1MB的内存可存储1MB/4B = 250000个整数,这样对于10000000个整数需要40趟排序来完成,此时改程序不需要借用磁盘空间,于是整个流程可如下图所示:

然而该程序具有的代价则是读取输入文件40次!

(3)那么,除了以上两种解决方式,还有没更好的举措呢?既不需要利用磁盘空间又不需要多读取输入文件。这就引入了一种位图解决方案,我们输入数据是1000万个7位十进制整数,我们可以使用一个1000万个位的字符串来表示这个文件,其中当且仅当整数i在文件中存在时,该位为1。感觉有点不对哈?确实,1MB只有800万个位,存储空间不足呀,我们需要1.25MB的存储空间呢。对于这个问题,一种方式是寻找出200万的稀疏位,或者采取两趟排序,就可以得以解决了,纵观性能,还是符合当下条件的。位图解决方案的流程如下图所示:

整个程序分三个步骤完成:

  • 初始化所有位为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

3. 动手之前驻足思考

从以上案例,我们可以看出一个道理“磨刀不误砍柴工”,对面临的实际问题仔细斟酌分析,精巧的编程可以得到更加稳固,性能更好的代码。

所以,在任何一个程序员面临实际问题的解决时,一定要注意:

  • 明确正确的问题
  • 牢记位图数据结构,该数据结构不仅可以应用在排序问题,它在其它应用也经常可作为高效率解决方案
  • 内存不足时,可以考虑多趟算法
  • 时间-空间的折中与双赢
  • 简单的设计,简单程序相比繁杂程序通常更加安全、可靠,更健壮、高效且易于维护

4. 习题分析解答

问题1. 本题要求利用具有库的语言实现排序,在我熟悉的语言即是C和C++了,C语言库“stdlib.h”中有qsort函数,而C++中标准容器库set本身就是一个无重复数据且升序排列元素的容器,具体实现如下:


/*
*   《编程珠玑》习题1.6
*   习题1:如果不缺内存,使用一个具有库的语言实现一种排序算法以表示和排序集合
*   在C语言中,使用qsort函数
*   函数定义于“stdlib.h”库中,定义为:void qsort(void *base,size_t nelem,size_t width,int (*Comp)(const void *,const void *));
*/

#include <iostream>
#include <cstdlib>
#include <set>

using namespace std;

//定义比较函数,确定排序模式
int comp(const void *x, const void *y)
{
    return *(int *)x - *(int *)y;
}

int arr[10000];
int main()
{
    int n = 0, i;
    while (cin >> arr[n])
    {
        n++;
    }

    qsort(arr, n, sizeof(int), comp);

    for (i = 0; i < n; i++)
    {
        cout << arr[i] << "\t";
    }
    cout << endl;
    system("pause");
    return 0;
}
/*
*   《编程珠玑》习题1.6
*   习题1:如果不缺内存,使用一个具有库的语言实现一种排序算法以表示和排序集合
*   在C++语言中,使用标准程序库容器set 自动存储不重复升序数据
*/

int main()
{
    set<int> s;
    int temp;
    while (cin >> temp)
        s.insert(temp);

    set<int>::iterator siter;

    for (siter = s.begin(); siter != s.end(); siter++)
        cout << *siter << "\t";

    cout << endl;
    system("pause");

    return 0;
}

问题2/3/4. 此三个问题相结合,实现位图排序解决方案,位向量采用位逻辑运算实现:

/*
*   《编程珠玑》习题1.6
*   习题2 3 4 关联性习题
*
*/

#include <iostream>
#include <cstdlib>

using namespace std;

//2. 位逻辑运算实现位向量
#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 10000000
int a[1 + N / BITSPERWORD];

void _set(int i)
{
    a[i >> SHIFT] |= (1 << (i & MASK));
}

void clr(int i)
{
    a[i >> SHIFT] &= ~(1 << (i & MASK));
}

int test(int i)
{
    return a[i >> SHIFT] & (1 << (i & MASK));
}

//4. 得到[0 ,n)内k个非重复随机数
/*等学习到12章节,补充此处*/

//3. 主程序

int main(void)
{
    int i;
    for (i = 0; i < N; i++)
        clr(i);
    //随机数现采取用户输入方式,待12章学习完毕,补充随机数生成算法
    while (cin >> i)
    {
        _set(i);
    }

    for (i = 0; i < N; i++)
    if (test(i))
        cout << i << "\t";

    system("pause");
    return 0;
}

问题5. 采取两趟排序。

问题6. 我们之前的案例是无重复数据出现,当数据重复时,我们依然可以采用位图解决方案,只不过不能采用1bit来记录出现次数了,当2bit时可记录< 4次 , 3bit 可记录<8次,那么当最多不出现10次时,就可以采用4bit来记录每一个数据的出现次数,此时需要增大内存使用量,若内存空间有限制,只能针对数据量采用多趟位图解决问题。

习题7-12. 每人有不同的见解,我也只是参考了下答案,才有所思考,此处就不重复答案书上的解法。

时间: 2024-11-09 00:51:14

《编程珠玑》阅读小记(1)— 开篇的相关文章

编程珠玑阅读笔记01

薄薄的一本书,丝毫无愧于珠玑两个字. 看了第一章,我对这本书佩服得五体投地.一个简洁的小例子,几个看似简单的算法,实际上包含了很多算法设计的思想.看完第一章,我对数据库的几种外排算法有了更深层次的理解 习题:位图和位向量来表示集合 例如集合{1,2,3,5,8,13}可表示为:`0 1 1 1 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0` (从左到右为第0~19位) 文中巧妙的对整数集合排序的思路分三步:  1. 数组所有位初始为0 2. 将输入的整数n当作index来用,出

《编程珠玑》备忘录(开篇)---当每个整数多次出现时

在开篇的习题部分,作者提出了一个延伸问题,即当每个整数最多出现10次的时候,这个排序问题应该如何解决. 尽管这次作者大方地没有限制我们可以使用的内存,但是在设计该程序时还应依照勤俭持家的原则:一个硬币掰开用.由于一个整数最多出现10次,那么我需要4位(bits)来对每个整数出现的频数进行统计.按照一般的设定,一个字符(char)往往包含了8个位,联想到作者之前使用位逻辑运算实现位向量的方法,我在这里照葫芦画瓢,使用一个字符的前后4位分别用于统计两个相邻整数出现的次数. 为了表示起来比较方便,我这

《编程珠玑》阅读小记(9) — 取样问题

问题 本章研究的问题是取样问题,也就是程序设计中的随机数,问题描述如下: 程序的输入包含两个整数m和n,其中 m < n:输出是0~n-1范围内m个随机整数的有序列表,不允许重复.从概率的角度看,我们希望没有重复的有序选择,其中每个选择出现的概率相等. 条件假设: 我们假设有一个能返回很大的随机整数(远远大于m 和 n )的函数bigrand(),以及一个能返回i-j范围内均匀选择的随机整数的randint(i,j). 本章关于这个问题提供了三种算法,接下来详细叙述每个算法的程序实现. 算法1

《编程珠玑》阅读小记(11) — 堆

章节简述 本章主要介绍堆,用该数据结构解决下面两个重要的问题: 排序,采用堆排序算法对n元数组排序,所花的时间不会超过O(nlogn),而且只需要几个字的额外空间: 优先级队列,堆通过插入新元素和提取最小元素这两种操作来维护元素集合,每个操作所需的时间都为O(logn): 本章采用自底向上的组织结构,从细节开始逐步过渡到正题. 堆数据结构 该部分介绍堆数据结构的设计思想. 优先级队列实现向量排序算法 优先级队列提供了一种简单的向量排序算法,优先在优先级队列中依次插入每个元素,然后按序删除它们,程

《编程珠玑》阅读小记(4) — 编写正确的程序

本章简述 本章的主题是编写正确的程序,以一个二分搜索算法引入. 关于二分搜索 二分搜索的关键思想是如果t在x[0..n-1]中,那么它就一定存在于x的某个特定范围之内.该程序最重要的部分是大括号内的循环不变式,也就是关于程序状态的断言. 代码的开发是自上而下进行的(从一般思想开始,将其完善为独立的代码行),该正确性分析则是自下而上进行的,从每个独立的代码行开始,检查它们是如何协同运作并解决问题的. 关于循环是程序中比较重要的部分,关于其正确性的讨论分为3个部分,每个部分都与循环不变式密切相关.

《编程珠玑》阅读小记(6) — 算法设计技术

本章简述 通过前面第二章节的叙述,描述了算法设计对程序员的日常影响:算法上的灵机一动可以使程序更加简单.但是本章内容将会发现算法设计的一个不那么常见但更富于戏剧性的贡献:复杂深奥的算法有时可以极大地提高程序性能. 问题及简单算法 本章引入的问题来自一维的模式识别,问题的输入是具有n个浮点数的向量x,输出是输入向量的任何连续子向量中的最大和. 例如,如果输入向量包含以下 N = 10 个元素: arr[N] = { 31, -41, 59, 26, -53, 58, 97, -93, -23, 8

《编程珠玑》高清pdf版

下载地址:网盘下载 作者简介 编辑 Jon Bentley是位于新泽西州Murray Hill的朗讯贝尔实验室计算机科学研究中心的技术委员会委员,Jon自1998年就成为Dr. Dobb's Joumal杂志的特约编辑,他的"编程珠玑"专栏多年来一直是顶级学术杂志Communications of the ACM最风行的特色专栏之一,而本书正是建立在这些专栏的基础之上. 目录 编辑 第一部分 基础 第1章 开篇 1.1 一次友好的对话 1.2 准确的问题描述 1.3 程序设计 1.4

编程珠玑 第2版 pdf

下载地址:网盘下载 内容简介  · · · · · · 本书是计算机科学方面的经典名著.书的内容围绕程序设计人员面对的一系列实际问题展开.作者Jon Bentley 以其独有的洞察力和创造力,引导读者理解这些问题并学会解决方法,而这些正是程序员实际编程生涯中至关重要的.本书的特色是通过一些精心设计的有趣而又颇具指导意义的程序,对实用程序设计技巧及基本设计原则进行了透彻而睿智的描述,为复杂的编程问题提供了清晰而完备的解决思路.本书对各个层次的程序员都具有很高的阅读价值.. 多年以来,当程序员们推选

编程珠玑高清pdf高清版免费下载

下载地址:网盘下载 备用地址:网盘下载 作者简介编辑Jon Bentley是位于新泽西州Murray Hill的朗讯贝尔实验室计算机科学研究中心的技术委员会委员,Jon自1998年就成为Dr. Dobb's Joumal杂志的特约编辑,他的“编程珠玑”专栏多年来一直是顶级学术杂志Communications of the ACM最风行的特色专栏之一,而本书正是建立在这些专栏的基础之上.目录编辑第一部分 基础 第1章 开篇 1.1 一次友好的对话 1.2 准确的问题描述 1.3 程序设计 1.4