编程珠玑 第一章习题解答

来源:互联网 发布:黑客论坛源码 编辑:程序博客网 时间:2024/05/17 08:08
4.生成[0,n)的之间k个不重复的随机整数。
#include<iostream>#include<cstdlib>#include<algorithm>#include<cstdio>using namespace std;const int N = 10000000;const int K = 10000000;int randint( int l, int r ){    return rand() % ( r-l ) + l;}int a[ N ];int main(void){    for( int i = 0; i < N; i++ )        a[i] = i;    for( int i = 0; i < K; i++ ){        swap( a[i], a[ randint(i,N) ] );        printf("%d\n",a[i]);    }    return 0;}

5.如果1MB是严格控制的空间,如果数据有1.25MB的bit数目。那么应该是需要读取2次。可以将输入文件分成两分,第一份保存[0,5000000)的数,第二个文件保存[5000000,10000000)的数字,然后分别进行排序,所用内存就可以降到1MB以内。如果我们把文件分成k份(每份都存一定区间的数),那么就可以在nk的时间,n/k的空间内完成排序。

6.如果每个数据出现最多10次,那么需要4个bit位来统计一个数。可以类比第五题,可以分成4份,在n/4的空间内完成。同样,当保存数字信息的量变化时,分成更多份,就可以在更小的空间内完成。

7.如果某个数出现超过一次的话,会发生什么?

      会被忽略掉, 因为原来的程序本身就是用来处理只出现一次的情况的。

在这种情况下,如何修改程序来调用错误处理函数?

    while (scanf("%d", &i) != EOF)  
    if(test(i)) call_error_fun();  
    else set(i); 

 当输入整数小于零或大于等于n时,又会发生什么?

      会出现访问越界的情况。-1访问时,会访问a[-1]的31个bit位。

如果某个输入不是数值又如何?在这些情况下,程序该如何处理?

    输入可能是浮点数,或是字符什么的~~ 可以先读入字符串,再用atoi转换成为整形数,如果失败,则进行出错处理。

8.免费电话号码至少有800,878,888等,那么如何查看一个号码是否是免费号码。?

第一种方案:如果是一千万个电话号码都有可能成为免费号码,那么至需要1.25MB * (免费号码前缀个数)。

第二种方案:省空间,多次扫描文件:

                  1、首先扫描整个文件,看有哪个免费号码前缀。以及每个免费号码前缀下的号码个数。

                  2、设置区间映射表:比如800前缀有125个免费号码,找到最大的数,与最小的数,差值做为bit长度。

第三种方案:建立索引的方式来进行处理。以最后7位为索引,后面800,878什么的,为值。如果不是免费号码,应该是不用加入到这个hash表中。

9.本题值得一说。

初始化空间需要大量时间,不过我们的应用只需要其中一点点空间,比如1000000的位图,我们只用到其中的10位,怎样节省时间?题目中提示,可以用额外的正比于向量大小的空间。我当时直接看的答案,到晚上搜了搜才看懂。

解决方法使用了两个额外的向量,from和to,变量top。如果对i位置进行初始化,进行一下步骤:
from[i] = top; to[top]=i;data[i] = 0;top++;
from[i]=top的目的是将i在to中的索引放入to中,to[top]=i的意识是,这个top位置对应的是i,这时data就可以做相应的操作,然后top右移。
判断一个位置是否初始化过的条件是( from[i] < top && to[ from[i] ] == i ),from[i]<top的意思是from[i]对应的to中的位置已经被处理过了,但是from[i]可能是随机值,也只能会小于top,那么这时就需要第二个条件了,to[from[i]] == i的意思是,to[from[i]] 所指向的位置就是i,这种双向的指向性的内容确保了能确定i位置是否被初始化过。

10.类似于取快递,根据电话号码的最后一位或者最后两位进行分类,类似于哈希的思想,用顺序遍历来处理碰撞。不能用开头的原因是很多电话号码的前面都是一样的,比如手机号码都是以1开头的。而且最后一位的分布比较随机、均匀。

11.= =答案竟然说飞鸽传书。

12.铅笔? T.T ....

其中第9题可以输入再探讨一下:

9. One problem with trading more space to use less time is that initializing the space can itself take a great deal of time. Show how to circumvent this problem by designing a technique to initialize an entry of a vector to zero the first time it is accessed. Your scheme should use constant time for initialization and for each vector access, and use extra space proportional to the size of the vector.

Because this method reduces initialization time by using even more space, it should be considered only when space is cheap, time is dear and the vector is sparse.

提供的参考答案:

The effect of initializing the vector data[0..n-1] can be accomplished with a signature contained in two additional n-element vectors, from and to, and an integer, top. If the element data[i] has been initialized, then from [i] < top and to[from[i]] = i. Thus from is a simple signature ,and to and top together make sure that from is not accidentally signed by the random contents of memory.

Blank entries of data are uninitialized in this picture:

        data :  |  |3 |  |2  |   | 8 |   |   |

        from:|  |0|   |2 |   | 1 |   |  |

        to:     |1|5|3|    |   |    |    |   |

 

The variable top is initially zero; the array element i is first accessed by the code

        from[i] = top

        to[top]  = i

        data[i]  = 0

        top++

This problem and solution are form Exercise 2.12 of Aho, Hopcroft and Ullman's Design and Analysis of Computer Algorithms. It combines key indexing and a wily signature scheme. It can be used for matrices as well as vectors.

 


首先,我们需要明确问题是什么。

       在这里,我们有一个稀疏的数组需要访问,并且在第一次访问的时候将其初始化为0. 因为数组很大,并且需要访问的数组元素很稀疏,而程序要求的时间很宝贵。

       所以,我们不能直接将data数组的各个元素都初始化为0.

       我们需要做的是在第一次数组data中的某个元素的时候,将其初始化为0. 如果之后再次访问到该元素,应该能够判断其是否已经被初始化过,避免多次初始化从而覆盖数据。

       在提供的解决方案中,就使用了from,to两个向量和top变量来保存哪些变量已经被初始化了。

       当我们访问索引为i的data元素时,我们通过判断form[i]是否小于top,并且to[from[i]]是否等于i来判断元素是否被初始化过。

       需要注意的是我们使用to向量的原因是为了防止from中的未经初始化的数据刚好因为小于top而导致出现错误判断。也就是说,我们是不对from和to向量进行初始化的(因为我们连对data进行初始化的成本都不肯),所以无法判断from中的数据哪些是我们写入的,哪些是原来就有的。于是,我们就通过增加一个to数组,并且增加一个判断条件来保证我们的判断是正确的。

       当然,这样的保证也并不是绝对正确的。只是小概率事件,直接忽略了吧??(其实可以探讨一下这个概率到底是多少??)

综上:

       我们在访问data中索引为i的元素时,通过条件from[i]<top和to[from[i]] == i来判断该数据是否被初始化过。

       如果已被初始化过,就直接访问该数据;

       否则,使用如下的语句对其进行初始化,并且在from,to和top中保存该数据已被初始化过这个信息:

       from[i]=top;

       to[top]=i

      data[i] = 0

      top++.


原创粉丝点击