9月2号,无意发现中国大学Mooc网上浙大的数据结构课程开始了;激动的心情无以言表。因为之前学习过浙江大学翁恺老师的C语言进阶课程,收获颇多。浙大的计算机团队教学质量很高。在此也感谢翁恺老师激发了我对计算机的热爱。
行吧,下面开始记录这个星期所学的内容:
数据结构的定义并没有一个统一的标准:
- “数据结构是数据对象,以及存在于该对象的实例和组成实例的数据元素之间的各种联系。这些联系可以通过定义相关的函数来给出。”——Sartaj Sahni, 《数据结构、算法与应用》
- “数据结构是ADT” ——Clifford A.Shaffer,《数据结构与算法分析》
- “数据结构是计算机中储存、组织数据的方式。通常情况下,精心选择的数据结构可以带来最优效率的算法。”——Wiki
**但可以确定的是,数据结构常常和算法紧密结合在一起;**
一、解决问题方法的效率,跟数据的组织方式有关;
以图书馆存放书籍为例,每一本书就是需要进行操作的数据;每一个书架就是内存空间,书架有大有小,分配的内存也是如此;如何存档书籍就像算法,可以随便存放也可以按一定的顺序存放(顺序表),可是这样做造成的后果是不方便以后查询;你也可以分类存放(树),这样在查询的时候会更加方便;
二、解决问题方法的效率,跟空间的利用效率有关;
例子:实现一个函数PrintN,使得传入一个正整数为N的参数后,能顺序打印从1到N的全部正整数。
课程上用了两种方法来实现,分别采用:
-
- 简单的循环实现:
#include
void PrintN(int N)
{
int i;
for(i=1; i<=N; i++){
printf("%d\n",i);
}
return;
}
int main()
{
int num;
scanf("%d",&num);
PrintN(num);
return 0;
}
- 2.使用递归实现:
#include
void PrintN(int N)
{
if(N){
PrintN(N-1);
printf("%d\n", N);
}
return;
}
int main()
{
scanf("%d",&num);
PrintN(num);
return 0;
}
我们以打印1000000个数为例,运行的结果是,循环可以执行,但是递归会直接爆掉;
这是因为执行递归的数量过大,导致没有足够多的内存去执行这么多语句;
三、解决问题方法的效率, 跟算法的巧妙程度有关;
例子:写程序计算给定多项式在给定点x处的值:
f(x) = a0 + a1x + ... +an-1xn-1 + anxn
分析:题目也很简单;有两种办法实现:
- 1.使用题目中所给出的原格式实现:
double f( 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;
}
- 2.将题目中所给出的原格式变型:
double f( 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;
}
可以看出,两种方法所做的事情都是一样的,可是效率却是天壤之别;
下面我们来测试时间,测试时间需要用到头文件
注意:
1. clock() : 捕捉从程序开始运行到clock()被调用时所耗费的时间。这个时间单位是clock tick,即“时钟打点”。
2. 常数CLK_TCK(或CLOCKS_PER_SEC):机器时钟每秒所走的时钟打点数.
尝试了一下,在机器上CLOCKS_PER_SEC = 1000000;
所以应该这样写:
#include
#include
clock_t start, stop; /* clock_t是clock()函数返回的变量类型 */
double duration; /* 记录被测函数运行时间,以秒为单位 */
int main () { /* 不在测试范围内的准备工作写在clock()调用之前*/
start = clock(); /* 开始计时 */
MyFunction();
stop = clock(); /* 停止计时 */
duration = ((double)(stop - start))/CLK_TCK; /* 计算运行时间 */
/* 其他不在测试范围的处理写在后面,例如输出duration的值 */ return 0;
}
///x在1.1处的值;
#include
#include
#include
clock_t start, stop;
double duration;
#define MAXK 1e7/* 设置执行函数的次数,方便计算时间,否则因为执行太快而无法得到结果; */
#define MAXN 10 /* 多项式最大项数,即多项式阶数+1 */
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;
}
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;
}
int main () {
int i;
double a[MAXN]; /* 存储多项式的系数 */
for ( i=0; i
实际结果让我们吃惊,其中ticks的单位是1毫秒的1000倍(就是说把结果除以1000的单位是毫秒,ticks1 = 2191.854ms);duration表示执行的时间,单位是秒:
我们发现效率的差别是十分巨大的!!准确来讲,相差一个数量级!
另外,我查了下头文件中关于CLOCKS_PER_SEC和CLK_TCK的描述——来自百度百科:
VC++6.0中time.h下宏定义的常量。其值为1000。VC++6.0中该符号常量定义如下:
#define CLOCKS_PER_SEC 1000
但是在Macintosh系统中,我们可以看到这样的一段定义:
1. #if __DARWIN_UNIX03
2. #define CLOCKS_PER_SEC 1000000 /* [XSI] */
3. #else /* !__DARWIN_UNIX03 */
4. #include /* Include file containing CLK_TCK. */
5.
6. #define CLOCKS_PER_SEC (__DARWIN_CLK_TCK)
7. #endif /* __DARWIN_UNIX03 */
因此,我们千万不要认为这个常量就是1000,还应该查看一下自己的time.h文件来确认一下。
此外,VC++6.0中把TC2.0中time.h下宏定义的常量CLK_TCK也宏定义成CLOCKS_PER_SEC,这样TC2.0下值为18.2的CLK_TCK在VC6.0下值变为1000。该宏定义如下:
#define CLK_TCK CLOCKS_PER_SEC
CLOCKS_PER_SEC,它用来表示一秒钟会有多少个时钟计时单元,其定义如下:
#define CLOCKS_PER_SEC ((clock_t)1000)
可以看到每过千分之一秒(1毫秒),调用clock()函数返回的值就加1。
可以使用公式clock()/CLOCKS_PER_SEC来计算一个进程自身的运行时间。例如:
void elapsed_time()
{
printf("Elapsed time:%u secs.\n",clock()/CLOCKS_PER_SEC);
}
当然,你也可以用clock函数来计算你的机器运行一个循环或者处理其它事件到底花了多少时间:
#include “stdio.h”
#include “stdlib.h”
#include “time.h”
int main(void)
{
long i = 10000000L;
clock_t start, finish;
double duration;
/* 测量一个事件持续的时间*/
printf( "Time to do %ld empty loops is ", i );
start = clock();
while( i-- ) ;/*注意这行后面有个分号*/
finish = clock();
duration = (double)(finish - start) / CLOCKS_PER_SEC;
printf( "%f seconds\n", duration );
system("pause");
return 0;
}
这是一个求时间差的程序,那么为什么要除以CLOCKS_PER_SEC呢?
这是因为clock()是以毫秒为单位,要正确输出时间差需要把它换成秒,因此需要除以CLOCKS_PER_SEC。
clock()函数计算出来的是硬件滴答的数目,不是毫秒。在TC2.0中硬件每18.2个滴答是一秒,在VC++6.0中硬件每1000个滴答是一秒。
上面我们看到时钟计时单元的长度为1毫秒,那么计时的精度也为1毫秒,那么我们可不可以通过改变CLOCKS_PER_SEC的定义,通过把它定义的大一些,从而使计时精度更高呢?通过尝试,你会发现这样是不行的。在标准C/C++中,最小的计时单位是一毫秒。
在VC++6.0中可以用CLK_TCK替换CLOCKS_PER_SEC。
通过这个测试,我才知道在苹果电脑上运行程序的速度是有多快了,每秒打点次数1000000次。。。
抽象数据类型(Abstract Data Type):
数据类型:
- 数据对象集
- 数据集合相关联的操作集
抽象:
- 描述数据类型的方法不依赖于具体实现
- 与存放数据的机器无关
- 与数据存储的物理结构无关
- 与实现操作的算法和编程语言均无关
只描述数据对象集和相关操作集“是什么”,并不涉及 “如何做到”的问题