《编程珠玑》学习记录第一章开篇

开篇提出了一个排序问题

问题描述:在一个磁盘文件中存在10000000左右个正整数,不存在重复元素,每个数在区间[1,  10000000]中。

输出:将这些整数升序排列,并存到一个磁盘文件里。

约束:大约有1M的内存空间可用,运行时间在10秒左右。

分析:对于1千万左右的数字,我们至少要用4个字节来存储。如果同时将1千万个这样的数字读入内存,大约需要38M左右的内存,

这远远超过题目要求。这种情况下,需要对磁盘文件进行多趟的排序,这样的话代码会很复杂,而且很难满足时间要求。

解决方法:文中提出用位图(或者叫位向量)来表示集合。比如,数字集合{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}。可见将位图中第i位置为1,来表示数字i。如例子中位图的第1,2,3,5,8, 13位被置为1.

位图的优点

  1. 节省内存空间。对于题目中的1千万个数据,只需要1千万位(1M左右)的内存就可以满足要求。
  2. 省略了排序过程。位图本身就有顺序,因此满足时间要求。

相关代码:

这是书中给的参考代码:

#include <iostream>
#include <cmath>
using namespace std;

#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 10000000
unsigned int a[1 + N/BITSPERWORD] = {0}; 


//将位图的第i位置为1 
void set(unsigned int i){
	a[i >> SHIFT] |= (1 << (i & MASK));
}

//将位图的第i位置为0 
void clr(unsigned int i){
	a[i >> SHIFT] &= ~(1 << (i & MASK));
}

//检查位图第i位 
int test(unsigned int i){
	return a[i >> SHIFT] & (1 << (i & MASK));
}

int main(){
	
	set(32);
	cout << a[1] << endl;
	
	return 0;
}

这是我自己写的代码:

#define N 10000000
bool a[1 + N] = {0};

void myset(int i){
	a[i] = true;
}

void myclr(int i){
	a[i] = false;
}

int mytest(int i){
	return a[i] ? pow(2, i) : 0;
}

int main(){
	
	myset(5);
	cout << mytest(5) << endl;
	
	return 0;
}

问题扩展:

第五题:内存空间不足怎么办?原题中给的内存是1M,如果现在只有0.8M的内存,这时候需要怎么办呢?

解答:由于内存的限制使得不能一次将数据全部读入内存。可以进行多趟读取。比如,进行两趟读取。第一趟只处理1到5百万的数据,第二趟处理5百万到1千万的数据。这样一来,内存只需要0.5M(之前的一半), 而消耗的时间将是之前的两倍。

第六题:如果每个数字可能重复出现,且最多有10个相同的数字该怎么办?

解答:原题中的位图,对于每一位,0表示没有出现,1表示出现。如果一个数字可能出现多次,那么这个数字的状态将会有,出现0次,出现1次, 出现2次,,,出现10次的情况,一共会有11中状态,那么至少需要4位才能表示这11种状态。这种情况下,用4位表示一个数字出现的次数,内存的消耗将会是之前的4倍。

第十题:一个商店的客户名单中,用客户的电话号码为关键字来标记客户资料。请问怎么设计客户资料的数据库,使得对客户资料的插入和检索更为高效?

解答:对于电话号码来说,前面几位数字很有可能出现相同的情况,而最后两位是随机出现的。用10 * 10个箱子组成一个二维矩阵,矩阵的每一行(列)都表示0到9这十个数字,行、列组成电话号码的最后两位,将对应的用户资料放入对应的箱子中。这种思想类似于散列表的思想。通过简答的操作,就可以在效率上提高很多。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第一部分 编 程 技 术 第1章 性能监视工具 3 1.1 计算素数 3 1.2 使用性能监视工具 7 1.3 一个专用的性能监视工具 8 1.4 开发性能监视工具 10 1.5 原理 11 1.6 习题 11 1.7 深入阅读 12 第2章 关联数组 13 2.1 Awk中的关联数组 13 2.2 有穷状态机模拟器 16 2.3 拓扑排序 17 2.4 原理 20 2.5 习题 21 2.6 深入阅读 22 第3章 程序员的忏悔 23 3.1 二分搜索 24 3.2 选择算法 26 3.3 子程序库 28 3.4 原理 30 3.5 习题 31 第4章 自描述数据 33 4.1 名字—值对 33 4.2 记录来历 36 4.3 排序实验 37 4.4 原理 39 4.5 习题 39 第二部分 实 用 技 巧 第5章 劈开戈尔迪之结 43 5.1 小测验 43 5.2 解答 44 5.3 提示 44 5.4 原理 47 5.5 习题 48 5.6 深入阅读 49 5.7 调试(边栏) 49 第6章 计算机科学箴言集 51 6.1 编码 52 6.2 用户界面 53 6.3 调试 53 6.4 性能 54 6.5 文档 56 6.6 软件管理 56 6.7 其他 58 6.8 原理 58 6.9 习题 58 6.10 深入阅读 60 第7章 粗略估算 61 7.1 头脑热身 61 7.2 性能的经验法则 62 7.3 Little定律 64 7.4 原理 65 7.5 习题 66 7.6 深入阅读 67 7.7 日常速算(边栏) 67 第8章 人员备忘录 69 8.1 备忘录 69 8.2 原理 71 8.3 深入阅读 71 第三部分 人性化I/O 第9章 小语言 75 9.1 Pic语言 76 9.2 视角 79 9.3 Pic预处理器 81 9.4 用来实现Pic的小语言 83 9.5 原理 87 9.6 习题 88 9.7 深入阅读 89 第10章 文档设计 91 10.1 表格 92 10.2 三条设计原则 94 10.3 插图 94 10.4 文本 96 10.5 合适的媒介 98 10.6 原理 100 10.7 习题 101 10.8 深入阅读 101 10.9 次要问题目录(边栏) 101 第11章 图形化输出 103 11.1 实例研究 103 11.2 显示结果取样 105 11.3 原理 107 11.4 习题 108 11.5 深入阅读 110 11.6 拿破仑远征莫斯科(边栏) 110 第12章 对调查的研究 113 12.1 有关民意调查的问题 113 12.2 语言 114 12.3 图片 117 12.4 原理 119 12.5 习题 120 第四部分 算 法 第13章 绝妙的取样 123 13.1 取样算法一瞥 123 13.2 Floyd算法 124 13.3 随机排列 125 13.4 原理 127 13.5 习题 127 13.6 深入阅读 128 第14章 编写数值计算程序 129 14.1 问题 129 14.2 牛顿迭代 130 14.3 良好的起点 132 14.4 代码 133 14.5 原理 135 14.6 习题 135 14.7 深入阅读 137 14.8 数值算法的力量(边栏) 137 第15章 选择 141 15.1 问题 141 15.2 程序 142 15.3 运行时间分析 145 15.4 原理 148 15.5 习题 149 15.6 深入阅读 151 附录A C和Awk语言 153 附录B 一个子程序库 157 部分习题答案 165 索引 181

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值