【编程珠玑】第一章:开篇

第一题

问题:如果不缺内存,如何使用一个具有库的语言来实现一种排序算法以表示和排序集合?

解答:c++语言可以调用STL中的set容器来进行排序

#include <iostream>
#include <set>
using namespace std;
int main(void)
{ 
	set<int> S;
	int i;
	set<int>::iterator j;
	while (cin >> i)
	S.insert(i);
	for (j = S.begin(); j != S.end(); ++j)
	cout << *j << "\n";
	return 0;
}
8 7 3 5 ^Z
3
5
7
8
请按任意键继续. . .

第二题

问题:如果使用位逻辑(例如与、或、移位)来实现位向量。

解答:思路就是一个int占了32位,所以对数据i进行左移位(5位)就可以算出放到int数组的哪一个元素里面去,然后取余数进行存储,这一部分可以用与或非来实现。

/***********************************************************/
// 程序目的:使用位逻辑(例如与、或、移位)来实现位向量
// 日期:    2014-8-31 
// 作者:    spencer_chong
/***********************************************************/
#include <stdio.h>

#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 10000000
int a[1 + N/BITSPERWORD];//使用整型数组模拟定义1000万个位的数组  

//i>>SHIFT指的是右移5位,也就是除以32,指的是该位存在于那个数组中  
//i&MASK指的是i%32,剩下的数字为多少,1<<(i&MASK))表示1左移i&MASK位 
void set(int i)
{ 
    a[i>>SHIFT] |= (1<<(i & MASK)); return;
}

void clr(int i) // 初始化清零
{        
    a[i>>SHIFT] &= ~(1<<(i & MASK)); // 逐次将32为值清零
}

int test(int i) // 测试逻辑位置为n的二进制位是否为1 

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

int main()
{   
    int i;
    for (i = 0; i < N; i++)
        clr(i);
    int arr[11] = {0, 34, 2, 3, 31,1,63, 4, 62, 33, 64};
    for (i = 0; i < 11; i++)
        set(arr[i]);
    for (i = 0; i < N; i++)
    {
        if (test(i))
            printf("%d  ", i);
    }
	printf("\n");
	return 0;
}

<pre name="code" class="cpp">0  1  2  3  4  31  33  34  62  63  64
请按任意键继续. . .

 

第三题

问题:运行效率是设计目标的一个重要组成部分,所得到的程序需要足够高效。在你自己的系统上实现位图排序并度量其运行时间。该时间与系统排序的运行时间及习题1中排序的运行时间相比如何?假设n为10 000 000,且输入文件包含1 000 000个整数。

解答:这个是从网上找到的答案,结果和计算机本身有关,但是这个比例是基本可以保证的。

 System SortC++/STLC/qsortC/bitmaps
Total Secs893812.610.7
Compute Secs79282.4.5
Megabytes.87041.25

第四题

如果认真考虑了习题3,你将会面对生成小于n且没有重复的k个整数的问题。最简单的方法就是使用前k个正整数。这个极端的数据集合将不会明显的改变位图方法的运行时间,但是可能会歪曲系统排序的运行时间。如何生成位于0至n-1之间k个不同的随机顺序的随机数?尽量使你的程序简短且高效。

/***********************************************************/
// 程序目的:产生0-N之间k个不同的随机数
// 日期:    2014-8-31 
// 作者:    spencer_chong
/***********************************************************/
#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;

int main ()
{
//输入范围N和输出个数K,建立动态数组;	
	int n;int k;
	cout<<"Input the range N:"<<endl; cin>>n;
	cout<<"Output number K:"<<endl;  cin>>k;
	cout<<"Result is :"<<endl;

	int *randnum=new int[n];
//初始化数组每个元素为其索引
	for (int i=0;i<n;i++)
	{
		randnum[i]=i;
	}
//初始化种子点,rand()返回15位的数
	srand((unsigned)time(NULL)); 

	int temp=0;
	int i,j;
	for(i=0;i<k;i++)
	{
		j=i+rand()/double(RAND_MAX)*(n-i-1);
		temp=randnum[i];
		randnum[i]=randnum[j];
		randnum[j]=temp;
		cout<<randnum[i]<<endl;
	}
	delete[] randnum;
	return 0;
}
Input the range N:
15
Output number K:
5
Result is :
4
13
5
9
8
请按任意键继续. . .

第五题

问题:那个程序员说他又1MB的可用存储空间,但是我们概要描述的代码需要1.25MB的空间。他可用不费力气的索取到额外的空间。如果1MB空间是严格的边界,你会推荐如何处理呢?

答案:只需要把10^7分成两批,分别是0-4999999以及5000000-999999,然后分别利用位图数据结构的方法进行排序。这里其实是时间和空间的权衡,将花费更多的时间来达到减少空间的目的。

第六题

问题:如果那个程序员说的不是每个整数最多出现一次,而是每个整数最多出现10次,你又如何建议他呢?你的解决方案如何随着可用存储空间总量的变化而变化呢?

解答:如果是最多出现10次,因为2^3<10<2^4,所以需要用半个字节(4位)来表示,这样子存储空间可能又不够了。参考第五题,可以把空间向时间转换,空间少用k倍,时间多花k倍。

第七题

问题:[R.Weil]所勾画的程序有几个缺陷。首先,它假定所输入的整数不会重复出现。如果某一个整数出现多次,会发生什么情况呢?在那种情况下,如何可以对程序进行修改以调用错误函数?输入整数小于0、大于或等于n时会发生什么情况?如果不是数字,那怎么办?程序应该怎么处理这些情况?程序还可采用其他什么稳健的检测措施呢?请描述一下测试该程序的各种小数据集,包括对这些情况以及其他行为不规则情况的适当处理。

解答:

1、如果出现多次: 程序只会当作只出现过一次。 要判断这种情况,只需在set(i)之前,用test(i)判断就行了。test(i)就是用于判断位置n出的逻辑位是否为1用的,如果是1表示重复过了。

2、输入超过范围,会引起数组下标访问越界问题

3、如果不是数字:只要这个值不是[0,N)的范围的话,都有可能发生越界问题。

第八题

问题:当那个程序员解决该问题的时候,美国所有的免费电话的区号是800。现在免费电话的区号包括800、877和888,而且还在增多。如何在1MB空间内完成对所有这些免费电话的号码的排序?如何将免费电话号码存储在一个集合中,要求可以实现非常快速的查找以判定一个给定的免费电话号码是否可用或者已经存在?

解答:(个人认为)这道题空间是给定了,但是时间说要非常快速,所以这个标准有点难以拿捏。区号是3位,那么做一般化考虑的话,就可能有1000种可能了。按照的方法,那么1MB的空间就要分1000位给区号用了,速度比之前慢1000倍,这速度可能无法满足要求。如果免费区号比较少的话,那么就可以分少一些出来用,例如知道只有3种,那么就分三位出来就行了,速度自然可以提高。或者按照区号进行分组,分别算出来再合并结果。

第九题

问题:使用更多的空间来换取更少的运行时间存在一个问题:初始化空间本身需要消耗大量的时间。说明如何设计一种技术,在第一次访问向量的项时将其初始化为0。你的方案应该使用常量时间进行初始化和向量访问,使用的额外空间应正比于向量的大小。因为该方法通过进一步增加空间减少初始化的时间,所以仅在空间很廉价、时间很宝贵且向量很稀疏的情况下才考虑。

解答:(答案来自互联网)

       这个以空间换时间的数据结构是很Amusing的。首先应考虑初始化数组导致的直接结果是什么? 就是第一次访问某个元素的时候,这个元素的值是初始化后的值。由此就想到了,我们提出的算法应该能够实现:当我们第一次访问某个元素的时候,这个元素的值是初始化后的值。这个实现的关键点是:如何判断是第一次访问,即如何判断数组中的元素是初始化过还是没有初始化过。思考如下几点:

1)当时我看这道题的时候第一个反应就是:再建立一个数组,然后第一次访问时候将已初始化元素的数组下标存储在里面,每次访问的时候查询这个数组,要是数组下标在里面,就说明这个数组下标的元素已经被初始化了。相信很多人都有同样的想法,但是如果这样做的话,虽然节省了初始化的时间,但是每次访问都要在存已访问元素下标的数组里进行线性搜索,这样的时间也是浪费不少,如何不用线性时间而用常数时间就能判断是否一个数组元素是否初始化过呢

2)然后我就又想到,还不如建立一个和需要访问数组等长的数组,在访问某个元素的时候先看这个数组,要是这个数组的对应元素标记为1则表示已经初始化过了,没有就把相应元素置为1。不过同样 ,这个等长的数组没有被初始化过,那么它里面存储的随机数有很大的概率就是1,这样很容易误判。

3)然后我看了习题答案后,恍然大悟。作者通过巧妙地利用from和to数组以及整数top保障了方法的可靠性。

       分析:首先,我们应该对题目进行彻底分析---我们需要访问的是一个长度(假设为n)非常大的数组,一般而言对数组中某个元素访问前我们必须要进行初始化,但是当n值非常大而程序对time要求较严格时,对所有的数组元素都进行统一的初始化是不可取的。为了达到程序对time的要求,我们应该对需要访问的元素(它的个数相对于n来说很小)进行初始化。其次,对元素初始化的判断---为了提高判断的准确性,答案引入了两个数组from和to以及整数top,且对于元素data[i]已初始化的条件是from[i]<top && to[from[i]]==i。现在让我们来具体分析这些规则是如何被应用的:假设当我们第一次访问的数组元素下标为1时,先判断初始化条件(此时数组from和to都没有初始化,当然time也不允许让我们多管闲事),一般而言from[1]中的随机数是大于top(现在为0)的,但我们不能保证,于是我们加入了第二个判断条件--to[from[1]]==1,对于这个表达式我们两次取随机值且让后者等于1,这样的概率有但几乎为0!因此,data[1]未被初始化,于是执行from[1]=top; to[top]=1; data[1]=0; top++;这样做的目的就是保证以后再次访问data[1]时不需要再初始化(条件满足了直接读取即可)。最后,对于该方法的可靠性分析---让我们先来分析一下整数top的作用,不难发现,top记录了当前data中已初始化元素的个数,但主要是保证了from中已初始化的元素都小于top(通过from[i]=top; top++),这给我们的判断条件(from[i]<top)提供了一定的可靠性,当然再加上第二到保险(to[from[i]]==i)使得此方法可靠性值得信赖!

       答案:借助于两个额外的n元向量from、to和一个整数top,我们就可以使用标识来初始化向量data[0....n-1]。如果元素data[i]已始化,那么from[i] < top并且 to[from[i]] =i.因此,from是一个简单的标识,to和top一起确保了from中不会被写入内存里的随机内容,变量top初始为0。下图中data的空白项未被始化:

from[i] =top;  
to[top] = i;  
data[i] = 0;  
top ++; 

       假如我现在要判断data[5]=8是否被初始化过,那么我先看to[from[5]]是否等于5,同理data[3]就判断to[from[3]]是否等于3就可以了。

第十题

问题:在成本低廉的隔日送达时代之前,商店允许顾客通过电话订购商品,并在几天后上门自取。商店数据库使用客户的电话号码作为其检索的主关键字(客户知道他们的电话号码,并且这些关键字几乎都是唯一的)。你如何组织商店的数据库,以允许高效的插入和检索操作?

解答:我们以电话号码最后两位作为散列索引, 那么其索引值就10 x 10大小数组中,如果有电话号码的后两位冲突,我们还是将他放到此数组中,这样效率会很高。在实际编程中对应的实现我们可以使用散列的链表解决法来解决上述碰撞冲突!




  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第一部分 基础 第1章 开篇 3 1.1 一次友好的对话 3 1.2 准确的问题描述 4 1.3 程序设计 4 1.4 实现概要 5 1.5 原理 6 1.6 习题 7 1.7 深入阅读 9 第2章 啊哈! 算法 11 2.1 三个问题 11 2.2 无处不在的二分搜索 12 2.3 基本操作的威力 13 2.4 排序 15 2.5 原理 16 2.6 习题 17 2.7 深入阅读 18 2.8 变位词程序的实现(边栏) 18 第3章 数据决定程序结构 21 3.1 一个调查程序 21 3.2 格式信函编程 23 3.3 一组示例 25 3.4 结构化数据 26 3.5 用于特殊数据的强大工具 27 3.6 原理 28 3.7 习题 29 3.8 深入阅读 30 第4章 编写正确的程序 33 4.1 二分搜索的挑战 33 4.2 编写程序 34 4.3 理解程序 36 4.4 原理 38 4.5 程序验证的角色 39 4.6 习题 40 4.7 深入阅读 42 第5章 编程小事 43 5.1 从伪代码到C程序 43 5.2 测试工具 44 5.3 断言的艺术 46 5.4 自动测试 48 5.5 计时 49 5.6 完整的程序 50 5.7 原理 51 5.8 习题 51 5.9 深入阅读 52 5.10 调试(边栏) 53 第二部分 性能 第6章 程序性能分析 57 6.1 实例研究 57 6.2 设计层面 59 6.3 原理 60 6.4 习题 61 6.5 深入阅读 61 第7章 粗略估算 63 7.1 基本技巧 64 7.2 性能估计 66 7.3 安全系数 68 7.4 Little定律 69 7.5 原理 70 7.6 习题 70 7.7 深入阅读 71 7.8 日常生活中的速算(边栏) 72 第8章 算法设计技术 73 8.1 问题及简单算法 73 8.2 两个平方算法 74 8.3 分治算法 75 8.4 扫描算法 77 8.5 实际运行时间 77 8.6 原理 79 8.7 习题 80 8.8 深入阅读 81 第9章 代码调优 83 9.1 典型的故事 83 9.2 急救方案集锦 84 9.3 大手术——二分搜索 88 9.4 原理 91 9.5 习题 92 9.6 深入阅读 94 第10章 节省空间 95 10.1 关键在于简单 95 10.2 示例问题 96 10.3 数据空间技术 99 10.4 代码空间技术 101 10.5 原理 103 10.6 习题 104 10.7 深入阅读 105 10.8 巨大的节省(边栏) 105 第三部分 应用 第11章 排序 109 11.1 插入排序 109 11.2 一种简单的快速排序 110 11.3 更好的几种快速排序 113 11.4 原理 115 11.5 习题 116 11.6 深入阅读 117 第12章 取样问题 119 12.1 问题 119 12.2 一种解决方案 120 12.3 设计空间 121 12.4 原理 123 12.5 习题 124 12.6 深入阅读 125 第13章 搜索 127 13.1 接口 127 13.2 线性结构 129 13.3 二分搜索树 132 13.4 用于整数的结构 134 13.5 原理 135 13.6 习题 136 13.7 深入阅读 137 13.8 一个实际搜索问题(边栏) 137 第14章 堆 141 14.1 数据结构 141 14.2 两个关键函数 143 14.3 优先级队列 145 14.4 一种排序算法 148 14.5 原理 150 14.6 习题 150 14.7 深入阅读 152 第15章 字符串 153 15.1 单词 153 15.2 短语 156 15.3 生成文本 158 15.4 原理 163 15.5 习题 163 15.6 深入阅读 164 第1版跋 165 第2版跋 167 附录A 算法分类 169 附录B 估算测试 173 附录C 时空开销模型 175 附录D 代码调优法则 181 附录E 用于搜索的C++类 187 部分习题提示 191 部分习题答案 195 索引 221
编程珠玑》是由美国计算机科学家Jon Bentley编写的一本经典书籍,第二版修订版pdf是该书的一种电子书格式。该书是一本关于编程问题和解决方法的指南,涵盖了算法、数据结构、程序设计等方面的知识。 第二版修订版pdf相比于第一版,进行了一些修订和更新。更新内容主要包括了一些新的编程问题和相应的解决方法,以及对一些已有问题的更详细的说明和讨论。此外,修订版还可能加入一些新的案例分析和实践经验,以帮助读者更好地理解和应用书中的内容。 作为一本经典的编程书籍,《编程珠玑》在计算机科学和编程领域具有很高的声誉和影响力。读者通过学习该书,可以提高自己解决编程难题的能力和思维方式,深入理解算法和数据结构的原理和应用,提升编程技巧和代码质量。 对于想要阅读《编程珠玑》的读者,选择第二版修订版pdf是一种方便和经济的方式。pdf格式可以在电子设备上阅读,无需携带纸质书籍,随时随地都可以学习。而且,修订版相对于第一版,内容更加全面和详细,更适合新手和有一定编程基础的读者阅读和学习。 综上所述,《编程珠玑》第二版修订版pdf是一本经典的编程书籍的电子格式,通过学习该书,读者可以提高自己的编程能力和思维方式,了解算法和数据结构的原理和应用。阅读第二版修订版pdf是一种便捷和经济的方式。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值