什么是数据结构
通过听浙大公开课和《数据结构与算法分析》这本书,我将从以下四点来展开,我对数据结构的认识。1.关于数据组织;2.关于空间使用;3.关于算法效率;4抽象数据类型(ADT)
1.关于数据组织
你有大量图书需要摆放,将有以下三种方法摆放:
1.随便放,放的时候一步到位,哪里有空哪里放,但查找的时候需要遍历每一本藏书;
2.按照书名的拼音字母顺序摆放,插入时候需要把每本书一个一个后移,让我插进来,但查找时候则二分查找,每次缩小一半范围;
3.先划分区域,分类(工科,理科,文科。农科等等),再按拼音字母顺序摆放,插入先确定类别,再在确定类别中移动每一本书,挪出空间,查找时候可以先确定类别,再在确定的类别中二分查找;
上面三种方法对比,无疑第三种方法要好很多,也是图书馆现在所使用的方法,但第三种方法任存在以下两种问题,空间该如何分配和类型该分多细,如空间分配过多,那么有大量的空间闲置浪费,空间分配过少,每次插入新书空间不够,就要增加书架,就很麻烦;同样地,类别应分多细,如类别分过粗,30万本书,分为理科,文科(理科里有化学,物理等等),这内部数据过多15万,任然难以处理;类别分过细,光类别分3万种,每种含10本书,内部数据很好处理,但3万的类别又处理不了。所以我们要找到合适的类别分类和空间分配。
解决问题方法的效率跟数据的组织方式相关
2.关于空间使用
对于任何一个简单的问题都至少有两种不一样的实现方法,下面例子将通过循环实现和递归实现
上述分别为循环实现和递归实现,令N=10,100,1000,10000,100000等,当N值较小时我们会得到相同的结果,但当N变大时,如N为100000时,会发生一下结果
我们会发现循环实现需要一段时间,但仍可得到我们想要的结果,而递归实现则直接出错,没有结果显示,因为递归对空间的利用很恐怖,堆积在栈中,数据过多,导致栈爆掉;
解决问题方法的效率跟空间利用率相关
关于算法效率
写程序计算给定多项式在给定点x处的值
C语言提供函数clock()和pow()
其中clock()在头文件<time.h>中,捕捉程序开始运行到clock()的调用之间所耗费的时间,时间单位为clock tick,即叫做时钟打点;(常定义clock_t start, stop 其中clock_t是clock ()函数的返回类型)
常数CLK_TCK:机器每秒钟所走的时钟打点数
pow()在头文件<math,h>中,用来求x的y次幂;
对于多项式一般而言有以下两种表达方式,如
F
(
x
)
=
∑
0
9
i
x
i
F(x) = ∑_0^9 ix^{i}
F(x)=0∑9ixi
多项式声明double f(int n, double a[], double x)
n指最高次数, a[ ]指由每一项前的系数组成的数组,x指未知数
通常采用下列方法2,原因后续解释
第一种方法一般方法,利用pow()函数,从次数从小到大递增
double f1(int n, double a[], double x){
int i;
double p = a[0];
for (i = 1; i <= n; i++)
p += (a[i] * pow(x, i));
return p;
}
第二种方法,每次向外提出一个x,即从内向外(次数由大到小递减)
double f2(int n, double a[], double x){
int i;
double p = a[n];
for (i = n; i > 0; i--)
p = a[i-1] + x*p;
return p;
}
源代码如下:
#include <stdio.h>
#include <time.h>
#include <math.h>
#define MAXN 10
clock_t start, stop;
double duration;
double f1(int n, double a[], double x);
double f2(int n, double a[], double x);
int main() {
double a[MAXN];
for (int i = 0; i < MAXN; i++)
a[i] = i;
start = clock();
f1(MAXN - 1, a, 1.1);
stop = clock();
duration = ((double)(stop - start)) / CLK_TCK;
printf("ticks1 = %lf\n", (double)(stop - start));
printf("duration1 = %6.2e\n", duration);
start = clock();
f2(MAXN - 1, a, 1.1);
stop = clock();
duration = ((double)(stop - start)) / CLK_TCK;
printf("ticks2 = %lf\n", (double)(stop - start));
printf("duration2 = %6.2e\n", duration);
return 0;
}
double f1(int n, double a[], double x) {
double p = a[0];
for (int i = 1; i <= n; i++)
p += (a[i] * pow(x, i));
return p;
}
double f2(int n, double a[], double x) {
double p = a[n];
for (int i = n; i > 0; i--)
p = a[i - 1] + x * p;
return p;
}
运行该程序,得到结果如下:
得到的结果没有差别,是因为两个函数跑太快,为了计算这两个函数跑一次所需要的时间,我们可以加入一个for循环,让它跑
1
0
7
10^7
107次,然后求平均值,便可求得一次所需时间
#include <stdio.h>
#include <time.h>
#include <math.h>
#define MAXN 10
#define MAXK 1e7 //被测函数重复调用次数
clock_t start, stop;
double duration;
double f1(int n, double a[], double x);
double f2(int n, double a[], double x);
int main() {
double a[MAXN];
for (int i = 0; i < MAXN; i++)
a[i] = i;
start = clock();
for (int i = 0; i < MAXK; i++) //for循环,重复调用10的7次方次
f1(MAXN - 1, a, 1.1);
stop = clock();
duration = ((double)(stop - start)) / CLK_TCK/MAXK;
printf("ticks1 = %lf\n", (double)(stop - start));
printf("duration1 = %6.2e\n", duration);
start = clock();
for (int i = 0; i < MAXK; i++)
f2(MAXN - 1, a, 1.1);
stop = clock();
duration = ((double)(stop - start)) / CLK_TCK/MAXK;
printf("ticks2 = %lf\n", (double)(stop - start));
printf("duration2 = %6.2e\n", duration);
return 0;
}
double f1(int n, double a[], double x) {
double p = a[0];
for (int i = 1; i <= n; i++)
p += (a[i] * pow(x, i));
return p;
}
double f2(int n, double a[], double x) {
double p = a[n];
for (int i = n; i > 0; i--)
p = a[i - 1] + x * p;
return p;
}
得到的结果出现一个数量级的差异
利用函数指针简化代码,源代码如下:
#include <stdio.h>
#include <time.h>
#include <math.h>
#define MAXN 10
clock_t start, stop;
double duration;
double f1(int n, double a[], double x);
double f2(int n, double a[], double x);
void f_get(double(* f)(int n, double a[], double x), double a[]);
int main() {
double a[MAXN];
for (int i = 0; i < MAXN; i++)
a[i] = i;
f_get(f1, a);
f_get(f2, a);
return 0;
}
void f_get(double(* f)(int n, double a[], double x), double a[]) {
start = clock();
(*f)(MAXN - 1, a, 1.1);
stop = clock();
duration = ((double)(stop - start)) / CLK_TCK;
printf("ticks = %lf\n", (double)(stop - start));
printf("duration = %6.2e\n", duration);
}
double f1(int n, double a[], double x) {
double p = a[0];
for (int i = 1; i <= n; i++)
p += (a[i] * pow(x, i));
return p;
}
double f2(int n, double a[], double x) {
double p = a[n];
for (int i = n; i > 0; i--)
p = a[i - 1] + x * p;
return p;
}
第一个算法比第二个算法慢了差不多一个数量级,这也就是我们选择方法2的原因。
解决问题的方法的效率跟算法的巧妙程度相关
4.抽象数据类型
什么是数据结构
数据对象在计算机中的组织方式;
1.逻辑结构:a.一对一:线性结构; b.一对多,树形结构; c.多对多,图型结构
2.物理存储结构:数组存储(连续存储),链表存储(任意存储)
数据对象必定与一系列加在其上的操作相关联;
完成这些操作所用的方法就叫算法;
抽象数据类型(ADT)
数据类型: a.数据对象集; b.数据集合相关联的操作集
数据类型在C语言里独立处理,在一些面向对象的语言,如C++,Java等,存在“类”, 把数据集和操作集封装在一个类里面
抽象:描述数据类型的方法不依赖于具体实现
1.与存放数据的机器无关
2.与数据存储的物理结构无关
3.与实现操作的算法和编程语言无关