数据结构的定义
数据:是描述客观事物的数和字符的集合
数据项:是具有独立含义的数据最小单位,也称为字段或域
数据对象:是指性质相同的数据元素的集合,它是数据的一个子集。
数据结构:是指所有数据元素以及数据元素之间的关系,可以看作是相互之间存在着某种特定关系的数据元素的集合。
数据结构通常包含以下几个方面:
(1)数据的逻辑结构:由数据元素之间的逻辑关系构成
(2)数据的存储结构:数据元素及其关系在计算机存储器中的存储表示,也称为数据的物理结构。
(3)数据的运算:施加在该数据上的操作
逻辑结构的类型
-
集合:指数据元素之间除了“同属于一个集合”的关系以外别无其他关系
-
线性结构:指该数据结构钢中的数据元素之间存在一对一的关系
-
树形结构:指该结构中的数据元素之间存在一对多的关系
-
图形结构:指该结构中的数据元素之间存在多对多的关系
存储结构的类型
-
顺序存储结构
采用一组连续的存储单元存放所有的数据元素。即所有数据元素在存储器中占有一整块存储空间,而且两个逻辑上相邻的元素在存储器中的存储位置也相邻。
优点:存储效率高,通过下标来直接存储
缺点:不便于数据修改,对元素的插入或删除操作可能需要移动一系列的元素
-
链式存储结构
每个逻辑元素用一个内存结点存储,每个结点是单独分配的,所有的结点的地址不一定是连续的。通过指针域将所有结点链接起来。
优点:便于数据修改,对元素进行插入或删除操作只需修改相应结点的指针域,不必移动结点。
缺点:存储空间的利用率低,不能对元素进行随机存取。
-
索引存储结构
指在存储数据元素信息的同时还建立附加的索引表。存储所有数据元素信息的表称为主数据表,其中每个数据元素有一个关键字和对应的存储地址。
优点:查找效率高。
缺点:需要建立索引表,增加了空间开销。
-
哈希(或散列)存储结构
根据元素的关键字通过哈希(或散列)函数直接计算出一个值,并将这个值作为该元素的存储地址
优点:查找速度快
一般适合要求对数据能够进行快速查找和插入的场合
数据类型
除去C语言中常用的 int(整型)、bool(布尔型)、float(浮点型)、double(双精度浮点型)、char(字符型)以及指针类型外,还需用到结构 体类型、自定义类型等。
-
结构体类型是由一组被称为结构体成员的数据项组成的,每个结构体成员都有自己的标识符,也称为数据域。一个结构体类型中所有成员的数据类型可以不相同。如下声明一个Student结构体类型:
struct Student //学生结构体类型 { int sno; //学号,占4个字节 char name[8]; //姓名,字符型数组占8个字节 int age; //年龄,占4个字节 }
-
自定义类型在C语言中允许使用typedef关键字来指定一个新的数据类型名,例如:
typedef char ElemType; //将char类型与ElemType等同起来
也可将代码较长的结构体类型声明用自定义类型标识符来代替,例如:
typedef struct Student { int sno; char name[8]; int age; }NewType; //用NewType别名表示Student结构体类型
在main函数中就能这样使用该类型定义变量:
NewType s1,s2; //等同于 struct Student s1,s2;
存储空间的分配
-
静态存储空间的分配方式
指在程序编译期间分配固定的存储空间的方式。该存储分配方式通常是在变量定义时就分配存储单元并一直保持不变,直至整个程序结束。以定义一个数组为例,如下所示:int str[20];
一旦遇到该语句,系统就会为str数组分配20个int型的整数空间。无论str中是否有元素,这片空间都一直被占用。它也属于自动变量,当超出其作用范围时系统自动释放其内存空间。
-
动态存储空间分配方式
指在程序运行期间根据需要动态地分配存储空间的方式。C语言中使用的是malloc()/free()函数对来进行分配和释放存储空间。
malloc():为一个指针变量分配一片连续的存储空间。
free():当不再使用这个存储空间时,用free()函数释放指针所指向的空间。
例如:char *p; p = (char*)malloc(10*sizeof(char)); //动态分配10个连续的字符空间 strcpy(p,"China"); //将“China”存放到p所指向的空间中 printf("%c\n",*p); //输出字符 C printf("%s\n",p); //输出字符串 “China” free(p); //释放p所指向的空间
p = (char)malloc(10sizeof(char));
详解:
p:p为指针变量
(char*):转换为字符指针
10:分配的字符个数
sizeof(char):求每个字符占用的空间大小
算法
算法是对特定问题求解步骤的一种描述,它是指令的有限序列。
算法具有5个特性:
(1)有穷性:一个算法必须总是在执行有穷步之后结束,且每一步都可在有穷时间内完成。任何不会终止的算法都是没 有意义的。
(2)确定性:相同的输入只能得出相同的输出,不能有二义性。
(3)可行性:算法中的所有操作都必须足够基本,算法可以通过有限次基本操作来完成其功能,也就是说算法中的每一 个动作能够被机械地执行。
(4)有输入:一个算法有零个或多个输入。
(5)有输出:一个算法有一个或多个输出。
算法设计的目标
(1)正确性:要求算法能够正确地执行预先规定的功能和性能要求。这是最重要也是最基本的标准。
(2)可使用性:要求算法能够很方便地使用。这个特性也叫用户友好性。
(3)可读性:算法的逻辑必须使清晰的、简单的和结构化的。
(4)健壮性:要求算法具有很好的容错性,即提供异常处理,能够对不合理的数据进行检查,不经常出现异常中断或死机现象。
(5)高效率与低存储量需求:通常算法的效率主要指算法的执行时间。算法的存储量指的是算法执行过程中所需的最大存储空间。效率和存储量都与问题的规模有关。
算法分析
算法分析就是分析算法占用计算机资源的多少。
算法时间性能分析
-
两种算法时间性能分析方法
- 事后统计法
编写算法对应程序,统计其执行时间。
计算机的运行速度、编写程序采用的计算机语言、编译产生的机器语言代码质量和问题的规模等都是影响 在计算机上执行所消耗时间的因素。
缺点:①必须执行程序。②存在很多因素掩盖了算法的本质。 - 事前估算法
仅考虑算法本身的效率高低,不考虑与计算机硬件、软件有关的因素。可以认为是一个特定的“运行工作量”的大小只依赖于问题的规模(通常用整数n表示)。
- 事后统计法
-
算法时间复杂度分析
一个算法是由控制结构(顺序、分支和循环3种)和原操作(指固有数据类型的操作)构成的。
算法的执行时间取决于控制结构和原操作的综合效果。算法时间复杂度也称为渐进时间复杂度,它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同。
求算法时间复杂度时通常只求T(n)的最高阶而省略其低阶项和常系数。例:T(n) = 2n²+2n+1 = O(n²) 说明该算法的时间复杂度时O(n²)。一般情况下,一个没有循环(或者有循环,但循环次数与问题规模n无关)的算法中原操作执行次数与问题规模无关,记作O(1),也称为常数阶。例如定义变量语句、赋值语句和输入输出语句,其执行时间都看成是O(1)。
各种不同的时间复杂度存在以下关系:O(1)<O(log~2~n)<O(n)<O(nlog~2~n)<O(n²)<O(n³)<O(2^n^)<O(n!)
练习
1.设计一个程序,输出所有小于等于n的素数(n为一个大于2的正整数)。
要求:
1)函数prime(n)的功能是判断正整数n是否为素数;
2)主函数调用该函数,每行输出10个素数。
#include<stdio.h>
#include<math.h>
int prime(int n);
int main()
{
int n,i,count=0;
printf("请输入n:");
scanf("%d",&n);
printf("小于等于100的素数:\n");
for(i=2;i<=n;i++)
{
if(prime(i))
{
printf("%5d",i);
count++;
if(count%10==0) //每行输出10个素数
{
printf("\n");
}
}
}
return 0;
}
int prime(int n){ //函数prime用来判断n是否为素数
int flag = 1; //使用flag进行标记,为flag=1说明n是素数 否则不是素数
for(int j=2;j<=sqrt(n);j++){
if(n%j==0)
{
flag = 0;
}
}
return flag;
}
2.编写一个程序,对于给定的正整数n,求1+2+3+…+n,采用逐个累加(累加法)与n(n+1)/2(高斯法)两种解法。
对于相同的n,给出这两种解法的求和结果和求解时间。
累加法
#include<stdio.h>
#include<time.h>
int main(){
double time;
int n,i,sum=0;
printf("请输入n:");
scanf("%d",&n);
clock_t begin = clock(); //计算开始的时间
for(i=1;i<=n;i++) //循环累加
{
sum+=i;
}
clock_t end = clock(); //计算结束的时间
time = (double)(end-begin) / CLOCKS_PER_SEC; //利用结束时间减去开始时间
printf("'累加法'求和结果为:%d\n",sum);
printf("'累加法'执行一次的时间为:%lf\n",time);
return 0;
}
高斯法
#include<stdio.h>
#include<time.h>
int main(){
double time;
int n,sum;
printf("请输入n:");
scanf("%d",&n);
clock_t begin = clock(); //计算开始的时间
sum = n*(n+1)/2; //"高斯法"求累加和
clock_t end = clock(); //计算结束的时间
time = (double)(end-begin) / CLOCKS_PER_SEC; //利用结束时间减去开始时间
printf("'高斯法'求和结果为:%d\n",sum);
printf("'高斯法'执行一次的时间为:%lf\n",time);
return 0;
}