《编程珠玑》笔记2 算法设计

这一章主要讨论算法设计这个主题。

1.问题

  A:给一个最多含有40亿(设为n)个随机排列的32位整数的顺序文件,找出一个不再文件中的数。(若具有足够内存?若只有几百字节的内存可用?)

  首先,2^32 = 4294967296 > 40亿,所以一定缺失数。40亿个数字放入内存,每个四字节(unsigned int),需要(4*10^8)/(8*10^6) = 500MB.

  B: 将一个n元向量左旋转i个位置,如n=8,i=3,向量abcdefgh旋转为defghabc.

  C: 给定一个英语词典,找出所有变位词集合,变位词如pots, stop, tops为一组变位词。其中每个单词通过改变其他单词中的字母顺序得到。

2.解法

简单来想,对这三个问题,

  A. 可以通过建立一个大小为 2^32 的bool数组,用来表示相应的整数是否出现,进而,这种数组其实可以用第一章中学过的位图bitmap来实现,先对各位全部初始化为0。遍历输入文件,若某数出现,将该位置为1。最后,遍历一遍这个位图,所有为0的位就表示该位对应的索引为出现过。时间复杂度为 O(n)+ 2*L (L为定值,表示 2^32,乘以2表示一次初始化和一次遍历),空间上, (2^32)/(8*10^6) = 512 MB.

  这个方法的最大缺点,就是耗费内存。这时采用多趟算法,假如我们只有 500 B内存,就是说我们一次只能申请到 0~3999 个可用位(实际上加上其他开销,可能申请不到这么多),可以先遍历一趟文件,只测试在 0~3999 之间的整数,第二趟,测试 4000~7999 ,依次类推……。(如果题目要求只找到一个数就停止,那这种方法应该很不错。。)

  B. 简单方法就是每次左移一位,然后重复调用这个左移操作i次;

    比较好一点的方法是  Reverse(0, i-1); Reverse(i, n-1); Reverse(0, n-1);

  C. 第一次想到的是使用STL中的map,采用26位的数组作为键,然后对每一个单词,计算它所对应的数组,属于同一数组的放入一个map中,其值是属于该键的单词的链表(也可以用STL中的set来实现)


书上的解法如下:

  A:利用二分搜索的方法,从每个整数都是32位来看,

  第一趟,读取40亿个输入,测试每个数的最高位,把起始位为0的写入一个文件,把起始位为1的写入另一个文件;读取n个

  第二趟,对第一趟较小的文件(数目一定小于20亿),测试第二位。按照同样的分法;  读取n/2个

   ……

  最后一趟,一定会有一个文件为空,总时间: n+n/2+n/4+……, = 2n,

  排序文件并扫描,得到所有缺失的整数。

  B:提出了三种方法,最后一种与前面相同。

  //杂技算法
    //块交换递归算法
    若要交换ab得到ba,设a比b短,将b分成bl和br,另br长度等于a,我们的目标是得到bl br a,但可以先交换br和a,得到br bl a,这样只要交换br和bl即可。可以递归实现。
    //求逆算法,需要时间为O(n)
    char *str;
    void reverse(int st, int ed)    //表示从st到ed的位置求逆
    {
        char temp;
        for(; st < ed; st++; ed--)
        {
            temp = str[st];
            str[st] = str[ed];
            str[ed] = temp;
        }
    }
    void shift(int n, int i)
    {
        reverse(0, i-1);
        reverse(i, n-1);
        reverse(0, n-1);
    }

  C:肯定不能使用基本的比较方法,所以这里提出了 标识 的思想,我们确定字典中每一个单词的标识,然后把有相同标识的单词集中起来。关于标识的方法,一种就是基于确定排序的,也就是对每个单词的字母,按照字母表排序,把排序过后的字符串作为标识;另一种就是上面的,使用长为26的数组作为标识;

3.原理

排序:产生有序输出;为另一个程序做准备(通常为二分搜索程序)。

二分搜索:在有序查找中很常用。

标识:要进行分类时,选择一个好的标识。

4.习题

4.1给定一个单词,查找该单词在字典中的所有变位词。

  首先计算单词的标识, 然后顺序读取整个字典,计算每个单词的标识并比较;

  如果允许对字典预处理,可以使用map<标识,单词集>先预处理词典,使用二分搜索或hash的方法,直接找到相应的变位词集。

4.2仍可以使用bitmap,最好使用多趟算法,因为只要求找到一个即可

4.3

4.5使用求逆算法,最后一步加上对b的翻转:

  abc def gh —— cba def gh —— cba def hg —— gh fed abc —— gh def abc

4.6这里只是把名字的标识作为 按键编码 来表示。

  方法与4.1相同,先对名字文件预处理,然后返回对应标识的所有名字即可。

4.7对在磁带上的矩阵求转置:先按列排序,在按行排序。

4.8是否存在这样的k元子集,只要找到前k个最小元素即可。与TopK算法相同,TopK算法具体见《编程之美》2.5节,有多种方法:

  全部数据堆排序:O(nlogn)

  选择法:O(K*N),直接选择出前k个最小的数。

  维护k个元素的数组,读取n个输入,每次找到k元素数组中最大的数,然后新到来的数与该数比较。这样对k元素的数组,可以遍历找到最大的数,也可以用堆,相应的时间复杂度为O(n*logk)和O(n*k).

  4.10往灯泡中灌水。。

5.变位词程序实现

下面是一个用C++实现的简单的变位词程序

 1 #include<iostream>
 2 #include<fstream>
 3 #include<string>
 4 #include<cctype>    //tolower function
 5 #include<vector>
 6 #include<map>
 7 using namespace std;
 8 
 9 string getfg(const string & str)
10 {
11     string temp;
12     int count[26];
13     for(int i = 0; i < 26; i++)
14             count[i] = 0;
15     for(int i = 0; i < str.size(); i++)
16     {
17         int n = tolower(str[i]) - 'a';
18         count[n]++;
19     }
20 
21     for(int i = 0; i < 26; i++)
22     {
23         while(count[i] != 0)
24         {
25             char cur = (char)(i+'a');
26             temp.append(1, cur);
27             count[i]--;
28         }
29     }
30     return temp;
31 }        
32 
33 int main(int argc, char**argv)
34 {
35     ifstream fin(argv[1]);
36 
37     map<string, vector<string> > shuf;
38     string str, flag;
39     while(fin >> str)
40     {
41         flag = getfg(str);
42         if(shuf.find(flag) == shuf.end())
43         {
44             vector<string> ns;
45             ns.push_back(str);
46             shuf.insert(make_pair(flag, ns));
47         }
48         else
49             shuf[flag].push_back(str);
50     }
51 
52     map<string, vector<string> > ::iterator iter = shuf.begin();
53     while(iter != shuf.end())
54     {
55         vector<string>::iterator iv = iter->second.begin();
56         while(iv != iter->second.end())
57         {
58             cout << *iv++ << " ";
59         }
60         iter++;
61 
62         cout << endl;
63     }
64 }
posted @ 2012-09-01 19:23  dandingyy  阅读(551)  评论(0编辑  收藏  举报