C_数据结构_基本概念和术语
数据(Data):它是信息的载体,它能够被计算机识别、存储和加工处理。它是计算机程序加工的“原料”。
(它是人们利用文字符号、数字符号一家其他规定的符号对现实世界的事物及其活动所做的描述。)
数据元素(Data Element)是数据的基本单位。
有些情况下,数据元素也称为元素、结点、顶点、记录。
一个数据元素可以有若干个数据项(也可称为字段、域、属性)组成。
数据项是具有独立含义的最小标识单位。
数据结构(Data Structure):指的是数据之间的相互关系,即数据的组织形式,
(数据结构是由某一元素集合及该集合所有数据元素之间的关系组成)
包括三个方面的内容
1.数据的逻辑结构:数据元素之间的逻辑关系
分类:线性结构,非线性结构
2.数据的存储结构:数据元素及其关系在计算机存储器内的表示
四种基本存储方法:顺序存储,链式存储,索引存储,散列存储
3.对数据所施加的操作。
数据处理
数据处理之对数据进行查找、插入、删除、合并、排序、统计以及简单计算等的操作过程。
数据类型
数据类型是一组性质相同的值的集合,以及定义于这个值结合上的一组操作的总称。
算法
简单的说就是解决特定问题的方法。数值算法(解决数值问题的算法)反之非数值算法
数值算法:算术运算,非数值算法:比较和逻辑运算
算法的5个特点:有穷性、确定性、输入、输出、可行性。
算法的描述
一个算法可以用自然语言、数学语言或约定的符号来描述,也可以用计算机高级程序语言来描述(Pascal、C)
下面是一错误处理函数:
#include <stdlib.h> //其中有exit的说明 #include <stdio.h> //其中有标准错误stderr的说明 void Error(char * message) { fprintf(stderr,"Error:%s\n",message);//输出错误信息 exit(1); }
显然,选用的算法首先应该是“正确的”。此外考虑如下三点:
1.执行算法所耗费的时间;
2.执行算法所耗费的存储空间,其中主要考虑辅助存储空间;
3.算法应易于理解,易于编码,易于调试。
exp1:求两个n阶方阵的乘积C=A x B算法如下
复杂度T(n)=2n³+3n²+2n+1 limT(n)/n³=2;
#define n 100 void MatrixMultiply(int A[n][n],int B[n][n],int C[n][n]) { int i,j,k; (1) for(i=0;i<n;i++) n+1 (2) for(j=0;j<n;j++) n(n+1) (3) C[i][j]=0; n² (4) for(k=0;k<n;k++) n²*(n+1) (5) C[i][j]=C[i][j]+A[i][k]*B[k][j]; n³ }
时间复杂度
(1)时间频度
一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。
(2)时间复杂度
在刚才提到的时间频度中,n称为问题的规模,当n不断变化时,时间频度T(n)也会不断变化。但有时我们想知道它变化时呈现什么规律。为此,我们引入时间复杂度概念。
一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
在各种不同算法中,若算法中语句执行次数为一个常数,则时间复杂度为O(1),另外,在时间频度不相同时,时间复杂度有可能相同,如T(n)=n^2+3n+4与T(n)=4n^2+2n+1它们的频度不同,但时间复杂度相同,都为O(n^2)。
按数量级递增排列,常见的时间复杂度有:
常数阶O(1),对数阶O(log(2)n),线性阶O(n),
线性对数阶O(nlog(2)n),平方阶O(n^2),立方阶O(n^3),...,
k次方阶O(n^k),指数阶O(2^n)。随着问题规模n的不断增大,上述时间复杂度不断增大,算法的执行效率越低。
(3)算法的时间复杂度
若要比较不同的算法的时间效率,受限要确定一个度量标准,最直接的办法就是将计算法转化为程序,在计算机上运行,通过计算机内部的计时
功能获得精确的时间,然后进行比较。但该方法受计算机的硬件、软件等因素的影响,会掩盖算法本身的优劣,所以一般采用事先分析估算的算法,
即撇开计算机软硬件等因素,只考虑问题的规模(一般用用自然数n表示),认为一个特定的算法的时间复杂度,只采取于问题的规模,或者说它是
问题的规模的函数。
为了方便比较,通常的做法是,从算法选取一种对于所研究的问题(或算法模型)来说是基本运算的操作,以其重复执行的次数作为评价算法时间
复杂度的标准。该基本操作多数情况下是由算法最深层环内的语句表示的,基本操作的执行次数实际上就是相应语句的执行次数。
一般 T(n)=O(f(n))
O(1)<O(log2n)<O(n)<O(n log2 n)<O(n^2)<O(n^3)<O(2^n)所以要选择时间复杂度量级低的算法。
实例说明时间复杂度的计算:
时间复杂度:算法中基本操作重复执行的次数是问题规模n的某个函数f(n),T(n)=O(f(n))。它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同。
语句的频度:是该语句重复执行的次数。
例1:交换i和j的内容。
temp=i; i=j; j=temp;
以上三条语句的频度均为1,该程序的执行时间是与问题规模n无关的常数,因此算法的时间复杂度为常数阶,记作T(n)=O(1)。
例2:变量计数。
(1) x=0;y=0;
(2) for(k=1;k<=n;k++)
(3) x++;
(4) for(i=1;i<=n;i++)
(5) for(j=1;j<=n;j++)
(6) y++;
以上语句中频度最大的语句是(6),其频度为f(n)= n2,所以该程序段的时间复杂度为T(n)=O(n2)
例3:求两个n阶方阵的乘积C=A×B,其算法如下:
#define n 100
void MatrixMultiply(int A[n][n],int B[n][n],int C[n][n])
{
int i,j,k
for (i=1;i<=n;++i) /* 次数 n+1 */
for (j=1;j<=n;++j) /* 次数 n*(n+1)*/
{
C[i][j]=0; /* 次数 n2 */
for (k=1;k<=n,k++) /* 次数 n2(n+1) */
C[i][j]=C[i][j]+A[i][k]*B[k][j];/* 次数 n3 */
}
}
T(n)=2n3+3n2+2n+1
lim(T(n)/ n3)=2
T(n)=O(n3)
例4:
(1){++x;s=0;}
(2)for (i=1;i<=n;++i) {++x;s+=x;}
(3)for (j=1;j<=n;++j)
(4)for (k=1;k<=n;k++){++x;s+=x;}
(5)i=1; while(i<=n) i=i*2;执行次数f(n)与n的关系是n=2^f(n)
含基本操作“x增1,即语句++x”的语句的频度分别为1,n,n2和log2n
常见的时间复杂度,按数量级递增排列依次为:常数阶O(1),对数阶0(Log2n),线性阶O(n),线性对数阶0(nLog2n),平方阶O(n2),立方阶0(n3),指数阶O(2n)。通常认为,具有指数阶量级的算法是实际不可计算的,而量级低于平方阶的算法是高效的。