您的位置:首页 > 编程语言

编程珠玑之第一章:开篇(习题)泛览

2013-09-14 16:20 531 查看
修改:2014年12月31日 12:50:08

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

实现1:C++的vector容器sort函数(当然也可以是list, sort):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

/***********************************************************/

// 程序名称:SwapSort.cpp

// 程序目的:使用库函数实现排序(使用C++库的Sort,vector)

// 程序来源:

// 日期: 2013-9-14 9:28:30

// 作者: JohnnyHu

/***********************************************************/

#include <iostream>

#include <cstdlib> /* srand, rand */

#include <ctime> /* time */

#include <algorithm>

#include <vector>

#include <set>

using namespace std;

int main ()

{

srand(time(NULL)); // 初始化随机数种子

vector<int> vec;

for( int i = 0; i != 20; i++)

vec.push_back( rand());

cout << "排序之前: " << endl;

for(int i = 0; i != 20; i++) // 原始数据

{

if (i!=0 && i%5 == 0 )

cout << endl;

cout << vec[i] << "\t";

}

cout << endl;

sort(vec.begin(), vec.end());

cout << "排序之后: " << endl;

for(int i = 0; i != 20; i++) // 排序后

{

if (i!=0 && i%5 == 0 )

cout << endl;

cout << vec[i] << "\t";

}

return 0;

}
实现2:C++的Set容器

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

int main ()

{

srand(time(NULL)); // 初始化随机数种子

set<int> theset;

for( int i = 0; i != 20; i++)

theset.insert(rand());

set<int>::iterator it;

int i = 0;

cout << "输出数据: " << endl;

for(it=theset.begin(); it != theset.end(); ++it, ++i) // 输出数据

{

if (i!=0 && i%5 == 0 )

cout << endl;

cout << *it << "\t";

}

cout << endl;

return 0;

}
实现3:C语言的qsort函数:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

#define MAX 20

int compare (const void * a, const void * b);

int main(void)

{

int values[MAX] = {0};

srand(time(NULL)); // 初始化随机数种子

for (int i=0; i < MAX; i++)

values[i] = rand();

qsort(values, MAX, sizeof(int), compare);

for (int i=0; i<MAX; i++)

{

if (i!=0 && i%5 == 0 )

printf("\n");

printf("%d\t", values[i]);

}

return 0;

}

// 比较函数

int compare (const void * a, const void * b)

{

return ( *(int*)a - *(int*)b );

}

实现1(其他情况类似)输出结果如下:



习题2:如何使用位逻辑运算(例如与、或、移位)来实现位向量?

实现1(源码):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

/***********************************************************/

// 程序名称:BitSort.cpp

// 程序目的:位向量实现10000000个整数排序

// 程序来源:编程珠玑源码(bitsort.c -- bitmap sort from Column 1)

// 日期: 2013-9-13 9:28:30

// 作者: Jon Bentley JohnnyHu修改

/***********************************************************/

#include <stdio.h>

#define BITSPERWORD 32

#define SHIFT 5

#define MASK 0x1F

#define N 10000000

int a[1 + N/BITSPERWORD];

void set(int i)

{

int va = i>>SHIFT; // i除以32取整

int vb = 1<<(i & MASK); // 等于(1, 2^1, 2^2....2^31)

int vc = i & MASK; // 等于(0,2,3....31)

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);

//Replace above 2 lines with below 3 for word-parallel init

// int top = 1 + N/BITSPERWORD;

// for (i = 0; i < top; i++)

// a[i] = 0;

//while (scanf("%d", &i) != EOF)

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("%d ", test(i) ? 1:0 );

}

return 0;

}
输出结果:



实现2:用C++容器BitSet

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

#include <iostream>

#include <bitset>

#include <cstdlib> /* srand, rand */

#include <ctime> /* time */

#define COUNT 20

using namespace std;

int main()

{

const size_t max = 10000000;

int b = 0;

bitset<max+1> bit; // 默认初始化所有位为0

srand(time(NULL));

for (int i=0; i != COUNT; ++i)

{

bit.set(rand(), 1); // 指定位置设为1

}

int i=0;

for (int pos=0; pos != max; pos++)

{

if (bit.test(pos))

{

if(i!=0 && i%5 == 0 )

cout << endl;

cout << pos << "\t";

++i;

}

}

return 0;

}
输出结果:



注:这里提一点,由于 很大,所以可能会出现堆栈溢出的现象,在vs2012下,堆栈的默认保留大小是1MB, 如果设的常量值太大,如 句话出现堆栈溢出现象,别忘更改其默认值! 具体(Vs2012下):项目——属性——配置属性——链接器——系统——堆栈保留大小。

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

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

解析:首先解决“生成小于n且没有重复的k个整数” 下面是解决方案:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

/***********************************************************/

// 程序名称:nRandomNum.cpp

// 程序目的:生成0至n-1之间k个不同的随机顺序的随机整数

// 程序来源:编程珠玑源码(bitsortgen.c -- gen $1 distinct

//
integers from U[0,$2))

// 日期: 2013-9-15 9:02:42 JohnnyHu修改

// 作者: Jon Bentley

/***********************************************************/

#include<iostream>

#include <cstdlib> /* srand, rand */

#include <ctime> /* time */

#include<algorithm> /* std::swap*/

using namespace std;

const int N = 100;

const int K = 100;

int a[ N ];

int randint( int a, int b )

{// 这里RAND_MAX=32767

return a + (RAND_MAX * rand() + rand()) % (b + 1 - a);

}

int main()

{

srand((unsigned) time(NULL));

for( int i = 0; i < N; i++ )

a[i] = i;

for( int i = 0; i < K; i++ )

{

swap( a[i], a[ randint(i, N-1) ] );

cout << a[i] << "\t";

}

return 0;

}

注:这里,我们只要适当的修改K、N的值,就可得到我们所需的。由生成随机数的过程来看,首先我们先依照顺序,生成n大小的数组,索引值即数组值,然后我们再依照顺序将索引的值与生成的随机索引代表的值交换,整个过程就是利用交换的方式打乱数组中值的顺序,使其无序。类似与洗牌原理。
具体详见:编程珠玑之习题1.4: 生成不同的随机顺序的随机整数及存储与读取

习题9:答案中我们用了两倍于原始空间的去换取更少的运行时间。增加from和to数组都和都和data数组等大,利用一个已知的常数top.这里我们用from可以标识data中哪些索引所在位置已被初始化赋值,哪些还没有,to则可以标识data初始化索引的顺序。如答案中:我们初始化data数组索引的顺序是1—>5—>3;判断data[i]是否已被初始化,我们只需判断:

from[i] < top && to[from[i]] ==i; 这个表达式即可!!!

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

习题11:考虑方向:如何花费更少的时间?花费更少的money? 答案中的“飞鸽传书”确实雷人

,天马行空,却又不失情理之中!!!正如最后所说:问题的最终解决,是通过打破一个人概念的壁垒,进而去解决一个较简单的问题而实现的!所以,如何打破这些概念壁垒,是我们思索的重点,创新性的解决问题,是重中之重!

习题12:人家花费100万美元的财力物力,你却这里只用了一个几美分的铅笔,如果美国宇航局(NASA)听了这则消息就

,汗!!!这个故事给我们一个启示:简洁高效的解决问题才是王道!完美的解决方案最讲究的莫过于追求致简之道!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: