编程珠玑学习笔记三 第二章总结
第二章开头提出了三个非常好的问题:
1.给定一个最多包含40亿个随机排列的32位整数的顺序文件,找出一个不在文件中的32位整数。
在内存足够的情况下,可以用位图来解决这个问题,位图中标记为0对应的数就是所要找的。
当内存不足时,此时采用分治的思想:
按最高位(也可按低位)的值对所有整数分类,高位为0的一组,记为A,高位为1的一组,记为
B,分别统计A,B组元素的个数。此时若len(A)<2^31,则A组中必有漏掉的数(此时缺失的
数高位一定为0);同理可判断B中是否有遗漏。选择一个有遗漏的分组,再对它的次高位进行分类,
一直递归下去直到对最低位也进行了分类。此时缺失的数每一位的值均可以确定,找出了这个数。
2.将一个n元一维向量旋转i个位置。
书中给出了两个算法:
(1)杂技法。该方法很巧妙,但不太容易理解,不知道算法的作者是怎么想出来的。
(2)分块求逆。这个算法很好用,通过分别求逆后再求逆来得到最终的结果。
比如要将n元向量旋转i位,过程为:
reverse(0,i-1)
reverse(i,n-1)
reverse(0,n-1)
我的思路:可以用空间换时间的思想来解决。比如要旋转的n元向量为abcdefgh,要将它
旋转3位:defghabc
我们可以开辟一块新空间来再存储一份n元向量,将它们拼接,为abcdefghabcdefgh,则
所要求的旋转结果为这个字符串的子串,取s[3:len(n)+3)即为所求
3.给定一个英文字典,找出变位词的集合。
解决思路就是对每一个单词进行标记,使得互为变位词的单词有相同的标记,而不是变位词的单词
有不同的标记。
这里列出几种可用的标记方法:
(1)对每个单词进行字典序排序,则互为变位词的单词有着相同的排序(若单词量大,对每个单词排序比较耗时)
(2)将26个英文字母分别对应一个素数,则一个单词的标记为组成它的每个字母的素数乘积。这样保证了
变位词的标记时一样的,而非变位词有不同的标记。
但当一个单词长度比较长时,计算一个乘积结果,数据量也比较大。
(3)统计一个单词中各字母出现的次数,并以此作为标记索引。如mississippi对应i4m1p2s4,可以将1省略,为
i4mp2s4.
第二章的思考题总结:
1.没有时间进行预处理,则直接按该单词的标识值去查字典,标识相同的单词输出
如有时间和空间进行预处理,可以先对整个字典进行标识计算,将计算值作为索引hash到不同的数组中,并将标识相同
的单词(变位词)链在同一个链表中。
查找时按给定的单词去查索引表(第一个节点即为链表表头节点),然后一次取出变位词输出。
2.给定包含43亿个32位整数的顺序文件,如何找出一个出现至少两次的整数?
思路:32位整数大约有42亿多个整数,文件包含43亿个整数,则必定有重复。
若内存空间足够,可以用位图来解决。当一个数对应的位置已经为1,则它是重复的数字。
也可像第二章开头的问题一中的二分法思想来解决。
6.同变位词的思路
7.将4000*4000的矩阵转置。
书中给出的解决方案是对每个元素插入行号,列号,根据转置矩阵的特点,对原来的矩阵先按列排序,
再按行排序,得到结果矩阵。
8.给定一个n元实数集合,一个实数t和一个整数k,如何快速确定是否存在一个k元子集,其元素之和不超过t?
思路:如果能得到n元集合最小的k个实数,计算它们的和,若是小于t,则这k个数即为所求;若大于t,则不存在
这样的k元子集。
问题转化为求n元实数集合中k个最小元素的问题,也就是topk问题。很自然得想到用堆来解决:用集合中的前
n个元素构建一个大顶堆,将剩下的n-k个元素分别于堆顶元素比较大小,若小于堆顶,则用它替换堆顶元素,重新
调整成堆;若大于堆顶元素,则舍去。最后存在于堆中的k个元素即为所要求的最小的k个数。
9.假设进行了k次查找,则顺序查找时间为nk,二分查找时间为nlgn(排序时间)+xlogn(查找时间)
使得 nk>nlgn+xlogn