“《编程珠玑》(第2版)第1章”:课后习题
1. 如果不缺内存,如何使用一个具有库的语言来实现一种排序算法表示和排序集合?
1)可以使用C语言中的快速排序qsort(参考自cplusplus),具体代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* qsort example */ 2 #include <stdio.h> /* printf */ 3 #include <stdlib.h> /* qsort */ 4 5 int values[] = { 40, 10, 100, 90, 20, 25 }; 6 7 int compare(const void * a, const void * b) 8 { 9 return (*(int*)a - *(int*)b); 10 } 11 12 int main() 13 { 14 int n; 15 qsort(values, 6, sizeof(int), compare); 16 for (n = 0; n < 6; n++) 17 printf("%d ", values[n]); 18 return 0; 19 }
关于qsort,有一篇博文写的不错,可以参考。
2)可以使用C++标准模板库函数sort(参考自cplusplus),具体代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // sort algorithm example 2 #include <iostream> // std::cout 3 #include <algorithm> // std::sort 4 #include <vector> // std::vector 5 6 bool myfunction(int i, int j) { return (i < j); } 7 8 struct myclass { 9 bool operator() (int i, int j) { return (i < j); } 10 } myobject; 11 12 int main() { 13 int myints[] = { 32, 71, 12, 45, 26, 80, 53, 33 }; 14 std::vector<int> myvector(myints, myints + 8); // 32 71 12 45 26 80 53 33 15 16 // using default comparison (operator <): 17 std::sort(myvector.begin(), myvector.begin() + 4); //(12 32 45 71)26 80 53 33 18 19 // using function as comp 20 std::sort(myvector.begin() + 4, myvector.end(), myfunction); // 12 32 45 71(26 33 53 80) 21 22 // using object as comp 23 std::sort(myvector.begin(), myvector.end(), myobject); //(12 26 32 33 45 53 71 80) 24 25 // print out content: 26 std::cout << "myvector contains:"; 27 for (std::vector<int>::iterator it = myvector.begin(); it != myvector.end(); ++it) 28 std::cout << ' ' << *it; 29 std::cout << '\n'; 30 31 return 0; 32 }
3)可以利用C++标准模板库容器set来完成相同的功能:
摘自一篇博文对set容器的简介:实现了红黑树的平衡二叉检索树的数据结构,插入元素时,它会自动调整二叉树的排列,把元素放到适当的位置,以保证每个子树根节点键值大于左子树所有节点的键值,小于右子树所有节点的键值;另外,还得保证根节点左子树的高度与右子树高度相等。可以使用中序遍历(用迭代器)将键值按照从小到大遍历出来。
关于set容器更详细的介绍请参照cplusplus。
具体代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <set> 3 using namespace std; 4 5 int main() 6 { 7 int myints[] = { 65, 23, 75, 42, 13 }; 8 set<int> myset(myints, myints + 5); // use 'set' member function 'insert' is also ok. 9 10 set<int>::iterator itr = myset.begin(); 11 for (; itr != myset.end(); itr++) 12 { 13 cout << *itr << " "; // 13, 23, 42, 65, 75 14 } 15 16 return 0; 17 }
2. 如何使用位逻辑运算(如与、或、移位)来实现位向量?
请参考自上上篇博文。
3. 请参考自该题参考答案(P230)
4. 如果认真考虑了问题3,你将会面对生成小于n且没有重复的m个整数的问题。最简单的方法就是使用前k个正整数。这个极端的数据集合将不会明显地改变位图方法的运行时间,但是可能会歪曲系统排序的运行时间。如何生成位于0至n-1之间的m个不同的随机顺序的随机整数?尽量使你的程序简短且高效。
对于该题,本书的第12章有比较详细的解答。下边一一列出:
假设我们有一个能返回很大的随机整数(远大于m和n)的函数bigrand(),以及一个能返回i..j范围内均匀选择的随机整数的函数randint(i, j)。
1)如果要从r个剩下的整数中选出s个,我们以概率s/r选择下一个数。具体代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 void genknuth(int m, int n) 2 { 3 for (int i = 0; i < n; i++) 4 { 5 // select m of remaining n-i 6 if ((bigrand() % (n-i)) < m) 7 { 8 cout << i << endl; 9 m--; 10 } 11 } 12 }
2)在一个初始为空的集合里面插入随机整数,直到个数足够。伪代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 initialize set S to empty 2 size = 0 3 while size < m do 4 t = bigrand() % n 5 if t is not in S 6 insert t into S 7 size++ 8 print the elements of S in sorted order
3)用C++标准模板库容器set完成。具体代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 void gensets(int m, int n) 2 { 3 // repeated elements are not allowed in set 4 set<int> S; 5 while (S.size() < m) 6 S.insert(bigrand() % n); 7 8 set<int>::iterator itr; 9 for (itr = S.begin(); itr != S.end(); itr++) 10 cout << *itr << endl; 11 12 }
4)将包含整数0~n-1的数组顺序打乱,然后将前m个元素排序输出。伪代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 for i = [0, n) 2 swap(i, randint(i, n-1))
5)只打乱该数组的前m个元素。具体代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 void genshuf(int m, int n) 2 { 3 int i, j; 4 int * x = new int[n]; 5 for (i = 0; i < n; i++) 6 x[i] = i; 7 8 for (i = 0; i < m; i++) 9 { 10 j = randint(i, n - 1); 11 int t = x[i]; 12 x[i] = x[j]; 13 x[j] = t; 14 } 15 16 sort(x, x + m); 17 for (i = 0; i < m, i++) 18 cout << x[i] << endl; 19 20 delete[] x; 21 }
5. 那个程序员说他有1MB的可用存储空间,但是我们概要描述的代码需要1.25MB的空间。他可以毫不费力地索取到额外的空间。如果1MB空间是严格的边界,你会推荐他如何处理呢?你的算法的运行时间又是多少呢?
使用位图表示1000万个数需要1000万个位,或者说125万个字节。考虑到没有以数字0或1打头的电话号码,我们可以将内存需求降低为100万个字节。另一种做法是采用两趟算法,首先使用5 000 000/8=625 000个字节的存储空间来排序0~4 999 999之间的整数(通过判断归入该范围),然后在第二趟排序5 000 000~9 999 999的整数。k趟算法可以在kn的时间开销和n/k的空间开销内完成对最多n个小于n的无重复正整数的排序。
6. 如果那个程序员说的不是每个整数最多出现一次,而是每个整数最多出现10次,你又如何建议他呢?你的解决方案如何随着可用存储空间总量的变化而变化?
如果每个整数最多出现10次,那么我们就可以用4位的半字节来统计它出现的次数。