什么是数据结构?
在学习什么是数据结构之前我们要先了解一下信息什么是数据?
数据结构的相关概念
数据
数据:是描述客观事物的符号,是计算机中可以操作的对象,是能被计算机识别,并输入给计算机处理的符号集合。
数据对象
数据对象:是性质相同的数据元素的集合,是数据的子集。
数据元素
数据元素:是组成数据的、有一定意义的基本单位,在计算机中通常作为整体处理。也被称为记录。
数据项
数据项:一个数据元素是由若干个数据项组成,数据项是数据不可分割的最小单位。
数据结构的相关概念小结
数据结构
数据结构(Data Structure)是计算机存储、组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的集合。
同时,数据结构按照视点不同分为逻辑结构和物理结构。
逻辑结构
逻辑结构分为四种类型:集合结构,线性结构,树形结构,图形结构。
集合结构
集合结构:数据元素同属一个集合,单个数据元素之间没有任何关系。
线性结构
线性结构:类似于线性关系,线性结构中的数据元素之间是一对一的关系。
树形结构
树形结构:树形结构中的数据元素之间存在一对多的关系。
图形结构
图形结构:数据元素之间是多对多的关系。
物理结构
物理结构又成存储结构,分为顺序存储结构和链式存储结构。
顺序存储结构
顺序存储结构:是把数据元素放到地址连续的存储单元里面,其数据间的逻辑关系和物理关系是一致的。
链式存储结构
链式存储结构:是把数据元素存放在任意的存储单元里面,其中存储单元可以是连续的,也可以是不连续的。
数据结构小结
数据类型和抽象数据类型
数据类型
数据类型是指一组性质相同的值的集合及定义在此集合上的一些操作的总称。
一般包括整型、实型、字符型等原子类型外,还有数组、结构体和指针等结构类型。
抽象数据类型
抽象数据类型(Abstract Data Type,ADT):是指一个数学模型及定义在该模型上的一组操作。类似C语言中的结构体以及C++、java语言中的类。
通俗的讲,抽象数据类型,泛指除基本数据类型以外的数据类型。
抽象数据类型的表示与实现
抽象数据类型的概念与面向对象的思想是一致的。
什么是算法?
算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。
算法特性
有穷性、 确定性 、 可行性、 输入 、输出。
评价算法优劣的基本标准
正确性 、 可读性 、 健壮性 、 高效性。
算法效率
衡量一个算法的好坏,一般是从时间和空间两个维度来衡量的,即时间复杂度和空间复杂度。
好的算法还应该具备时间效率高和存储量低的特点。
算法+数据结构=程序
算法的度量方法
事后统计方法(不科学,不准确)
事前分析估算方法(常用)
时间复杂度
在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。一 个算法执行所耗费的时间。
一个算法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,为算法的时间复杂度。
例如:
void fun1(int n)
{
printf("时间复杂度\n"); //执行1次
return 0; //执行1次
}
根据以上代码,不管n为多少,都是T(n)=2。
void fun2(int n)
{
//执行 1次 n+1次 n次
for (int i = 0; i < n; ++i ) //共执行2n+2次
{
printf("时间复杂度\n"); //执行n次
}
return 0; //执行1次
}
根据以上代码,当执行n次时,T(n)=3n+3。
但是,作为衡量代码的执行速度的依据,当代码比较多的时候,使用T(n)就会很麻烦,计算的话会浪费大量时间和精力。所以,算法一般使用T(n)简化的估算值,来衡量代码的执行速度,这个简化的估算值叫做时间复杂度。
算法时间复杂度定义
在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。算法的时间复杂度,也就是算法的时间量度。
记作:T(n)=O(f(n)) 。
它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐近时间复杂度,简称为时间复杂度。
其中f(n)是问题规模n的某个函数。 这样用大写O( ) 来体现算法时间复杂度的记法,我们称之为大O记法。
一般情况下,随着n的增大,T(n)增长最慢的算法为最优算法。
推导大O阶
1.用常数1取代运行时间中的所有加法常数。 2.在修改后的运行次数函数中,只保留最高阶项。 3.如果最高阶项存在且不是1,则去除与这个项相乘的常数。得到的结果就是大O阶。
我们计算时间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要大概执行次数,所以我们常用大O的渐进表示法。
常数阶
当T(n) = 常数,则T(n) = O(1)
因此,上面那个例子T(n)=2的时间复杂度为O(1)。
线性阶
如果“ T(n)=常数 x n + 常数 ” ,可以直接忽略 + 号后面的常数,因为随着n的增大,“ 常数 x n ”会越来越大,而 “ + 常数 ”则保持不变,也就导致 “ + 常数 ” 相当于不存在,可以直接省略。然后“ 常数 x n ” 内常数可以直接估算为1,也可以理解为去掉这个作为系数的常数,所以,他的时间复杂度为n。
因此,上面那个例子T(n)=3n+3的时间复杂度为O(n)。
平方阶和立方阶(多项式)
T(n)=3n2+2n+1
T(n)=6n3+2n2+3n+4
只需要保留n的最高次项,也就是保留n的次方数最大的那一项。因为随着n的增大,后面项的增长远远不及n的最高次项大,所以直接省略低次项,接着最高次项系数也要去掉。
因此 “ T(n)=3n2+2n+1 ” 的时间复杂度为O(n2)
因此 “ T(n)=6n3+2n2+3n+4 ” 的时间复杂度为O(n3)
对数阶
来段代码帮助理解:
void fun3(int n)
{
for (int i = 0; i < n; i *= 2)
{
printf("时间复杂度\n");
}
return 0;
}
由此可见
当n=8时,T(8)=3 23=8 2T(8)=8
当n=16时,T(16)=4 24=16 2T(16)=16
所以 2T(n) = n,因此T(n) = O(logn)
常用的时间复杂度所耗费的时间从小到大依次是
O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2 n) < O(n!) < O(nn)
最好、最坏和平均时间复杂度
最好时间复杂度,指的是算法计算量可能达到的最小值。
最坏时间复杂度,指的是算法计算量可能达到的最大值。一般在没有特殊说明的情况下,都是指最坏时间复杂度。
平均时间复杂度,是指算法在所有可能情况下,按照输入实例以等概率出现时,算法计算量的加权平均值。
例如:
在一个长度为N数组中搜索一个数据x
最好情况:1次找到
最坏情况:N次找到
平均情况:N/2次找到
空间复杂度
空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时占用存储空间大小的量度
只需要分析辅助变量所占的额外空间即可。
空间复杂度:S(n) = O(f(n))
如果算法执行所需要的临时空间不随着某个变量n的大小而变化,即此算法空间复杂度为一个常量,可表示为 O(1)