【程序设计实践】第2章 算法和数据结构
2013-08-07 08:31
190 查看
第2章 算法和数据结构
即使是很复杂的程序,也是由简单的数组、表、散列表等简单东西堆砌起来的。
某一种快速排序:
/* quicksort: sort v[0]..v[n-1] into increasing order */
void quicksort(int v[], int n)
{
int i, last;
if (n <= 1) /* nothing to do */
return;
swap(v, 0, rand() % n); /* move pivot elem to v[0] */
last = 0;
for (i = 1; i < n; i++) /* partition */
if (v[i] < v[0])
swap(v, ++last, i);
swap(v, 0, last); /* restore pivot */
quicksort(v, last); /* recursively sort */
quicksort(v+last+1, n-last-1); /* each part */
}
/* swap: interchange v[i] and v[j] */
void swap(int v[], int i, int j)
{
int temp;
temp = v[i];
v[i] = v[j];
v[j] = temp;
}
排序算法花费的时间正比于nlogn。
typedef struct Nameval Nameval;
struct Nameval {
char *name;
int value;
};
struct Nvtab {
int nval; /* current number of values */
int max; /* allocated number of values */
Nameval *nameval; /* array of name-value pairs */
};
enum { NVINIT = 1, NVGROW = 2 };
/* addname: add new name and value to nvtab */
int addname(Nameval newname)
{
Nameval *nvp;
if (nvtab.nameval == NULL) { /* first time */
nvtab.nameval = (Nameval *) malloc(NVINIT * sizeof(Nameval));
if (nvtab.nameval == NULL)
return -1;
nvtab.max = NVINIT;
nvtab.nval = 0;
} else if (nvtab.nval >= nvtab.max) { /* grow */
nvp = (Nameval *) realloc(nvtab.nameval,
(NVGROW*nvtab.max) * sizeof(Nameval));
if (nvp == NULL)
return -1;
nvtab.max *= NVGROW;
nvtab.nameval = nvp;
}
nvtab.nameval[nvtab.nval] = newname;
return nvtab.nval++;
}
/* delname: remove first matching nameval from nvtab */
int delname(char *name)
{
int i;
for (i = 0; i < nvtab.nval; i++)
if (strcmp(nvtab.nameval[i].name, name) == 0) {
memmove(nvtab.nameval+i, nvtab.nameval+i+1,
(nvtab.nval-(i+1)) * sizeof(Nameval));
nvtab.nval--;
return 1;
}
}
在ANSI C的标准库里定义了两个相关的函数:memcpy的速度快,但是如果源位置和目标位置重叠,它有可能覆盖掉存储区中的某些部分;memmove函数的速度可能慢些,但总能保证复制的正确完成。
我们无法在编译时初始化一个非空的表,这点也与数组不同。表应该完全是动态构造起来的。
二分检索完全不能适用于表。
/* apply: execute fn for each element of listp */
void apply(Nameval *listp, void (*fn)(Nameval*, void*), void *arg)
{
for ( ; listp != NULL; listp = listp->next)
(*fn)(listp, arg); /* call the function */
}
要使用apply,例如打印一个表的元素,我们可以写一个简单的函数,其参数包括一个格式描述串:
/* printnv: print name and value using format in arg */
void printnv(Nameval *p, void *arg)
{
char *fmt;
fmt = (char *) arg;
printf(fmt, p->name, p->value);
}
它的调用形式是:
apply(nvlist, printnv, “%s: %x"n”);
要销毁一个表就必须特别小心:
/* freeall: free all elements of listp */
void freeall(Nameval *listp)
{
Nameval *next;
for ( ; listp != NULL; listp = next) {
next = listp->next;
/* assumes name is freed elsewhere */
free(listp);
}
}
即使是很复杂的程序,也是由简单的数组、表、散列表等简单东西堆砌起来的。
检索
顺序检索,二分检索排序
快速排序库
C函数库中的qsort,调用时必须提供一个比较函数。ANSI C的二分检索函数:bsearch,它也要求一个指向比较函数的指针。标准C++库中有个名为sort的类属算法。某一种快速排序:
/* quicksort: sort v[0]..v[n-1] into increasing order */
void quicksort(int v[], int n)
{
int i, last;
if (n <= 1) /* nothing to do */
return;
swap(v, 0, rand() % n); /* move pivot elem to v[0] */
last = 0;
for (i = 1; i < n; i++) /* partition */
if (v[i] < v[0])
swap(v, ++last, i);
swap(v, 0, last); /* restore pivot */
quicksort(v, last); /* recursively sort */
quicksort(v+last+1, n-last-1); /* each part */
}
/* swap: interchange v[i] and v[j] */
void swap(int v[], int i, int j)
{
int temp;
temp = v[i];
v[i] = v[j];
v[j] = temp;
}
排序算法花费的时间正比于nlogn。
可增长数组
定义一个可增长数组,新元素被加到有关数组的最后。在必要时将自动增大以提供新的空间。删除名字需要一点诀窍,如果元素顺序不重要,最简单的方法就是把位于数组的最后元素复制到这里。typedef struct Nameval Nameval;
struct Nameval {
char *name;
int value;
};
struct Nvtab {
int nval; /* current number of values */
int max; /* allocated number of values */
Nameval *nameval; /* array of name-value pairs */
};
enum { NVINIT = 1, NVGROW = 2 };
/* addname: add new name and value to nvtab */
int addname(Nameval newname)
{
Nameval *nvp;
if (nvtab.nameval == NULL) { /* first time */
nvtab.nameval = (Nameval *) malloc(NVINIT * sizeof(Nameval));
if (nvtab.nameval == NULL)
return -1;
nvtab.max = NVINIT;
nvtab.nval = 0;
} else if (nvtab.nval >= nvtab.max) { /* grow */
nvp = (Nameval *) realloc(nvtab.nameval,
(NVGROW*nvtab.max) * sizeof(Nameval));
if (nvp == NULL)
return -1;
nvtab.max *= NVGROW;
nvtab.nameval = nvp;
}
nvtab.nameval[nvtab.nval] = newname;
return nvtab.nval++;
}
/* delname: remove first matching nameval from nvtab */
int delname(char *name)
{
int i;
for (i = 0; i < nvtab.nval; i++)
if (strcmp(nvtab.nameval[i].name, name) == 0) {
memmove(nvtab.nameval+i, nvtab.nameval+i+1,
(nvtab.nval-(i+1)) * sizeof(Nameval));
nvtab.nval--;
return 1;
}
}
在ANSI C的标准库里定义了两个相关的函数:memcpy的速度快,但是如果源位置和目标位置重叠,它有可能覆盖掉存储区中的某些部分;memmove函数的速度可能慢些,但总能保证复制的正确完成。
表
单链表,构造表的最简单的办法就是把每个元素加在最前面。我们无法在编译时初始化一个非空的表,这点也与数组不同。表应该完全是动态构造起来的。
二分检索完全不能适用于表。
/* apply: execute fn for each element of listp */
void apply(Nameval *listp, void (*fn)(Nameval*, void*), void *arg)
{
for ( ; listp != NULL; listp = listp->next)
(*fn)(listp, arg); /* call the function */
}
要使用apply,例如打印一个表的元素,我们可以写一个简单的函数,其参数包括一个格式描述串:
/* printnv: print name and value using format in arg */
void printnv(Nameval *p, void *arg)
{
char *fmt;
fmt = (char *) arg;
printf(fmt, p->name, p->value);
}
它的调用形式是:
apply(nvlist, printnv, “%s: %x"n”);
要销毁一个表就必须特别小心:
/* freeall: free all elements of listp */
void freeall(Nameval *listp)
{
Nameval *next;
for ( ; listp != NULL; listp = next) {
next = listp->next;
/* assumes name is freed elsewhere */
free(listp);
}
}
树
二叉树,树的构造,树的遍历。散列表
常见做法是为每个散列值(或称桶)关联一个项的链表,即数组的每个元素是个链表,链接起具有该散列值的所有数据项。散列函数hash应该计算出什么东西。这个函数必须是确定性的,应该能算得很快,应该把数据均匀地散布到数组里。对于字符串,最常见的散列算法之一就是:逐个把字节加到已经构造的部分散列值的一个倍数上。乘法能把新字节在已有的值中散开来。这样,最后结果将是所有输入字节的一种彻底混合。根据经验,在对ASCII串的散列函数中,选择31和37作为乘数是很好的。小结
选择算法有几个步骤。首先,应参考所有可能的算法和数据结构,考虑程序将要处理的数据大概有多少。如果被处理数据的量不大,那么就选择最简单的技术。如果数据可能增长,请删掉那些不能对付大数据集合的东西。然后,如果有库或者语言本身的特征可以使用,就应该使用。如果没有,那么就写或者借用一个短的、简单的和容易理解的实现。如果实际测试说明它太慢,那么就需要改用某种更高级的技术。相关文章推荐
- 程序设计实践----算法与数据结构
- 程序设计实践 双语版3.1---马尔可夫链算法
- [Java基础巩固](0) --- 程序设计基础和基本数据结构, 算法
- 程序设计实践(2)----算法
- 【算法设计与数据结构】拓扑排序算法的实现——Kahn算法及基于dfs的算法
- 东南大学第四届逻辑算法程序设计竞赛决赛解题报告
- 数据结构上机实践第七周项目1 - 自建算法库——顺序环形队列
- ACM_程序设计竞赛:贪心算法:字典最小序
- 算法,设计模式,数据结构,多线程以及研究领域的深入是我研究生阶段要完成的任务
- XTU 程序设计实践模拟考试题1
- 算法设计题2.39~2.40-线性表-第2章-《数据结构习题集》-严蔚敏吴伟民版
- 程序设计基础及数据结构基础
- 数据结构上机实践第11周项目1 - 图基本算法库
- 算法设计和数据结构学习_1(一道堆排序作业题)
- c++程序原理设计与实践----(1)写在前面
- 中国象棋程序的设计与实现(十二)--棋盘绘制算法(尽管注释非常详细,完全理解仍有难度)
- 微软等数据结构+算法面试100题(5)--怎样编写一个程序,把一个有序整数数组放到二叉树中
- 设计好的数据结构对程序至关重要
- 【原创 Hadoop&Spark 动手实践 9】Spark SQL 程序设计基础与动手实践(上)
- 数据结构和算法设计(迷宫求解问题的栈和队列的实现)