第一章 数据结构与算法
数据结构
注:这是本人观看B站王卓老师讲的数据结构与算法的学习笔记
基本概念和术语
1.数据(Data)是客观事物的符号表示,是所有能输入到计算机中并被计算机程序处理的符号的总称(集合)。是信息的载体;
2.数据元素(DataElement)是数据的基本单位,在计算机中通常作为一个整体进行考虑和处理。在有些情况下,数据元素也称为元素、记录、节点、顶点等。
3.数据项(Data Item)是组成数据元素的、有独立含义的、不可分割的最小单位。例如,学生基本信息表中的学号、姓名、性别等都是数据项
![](https://i.loli.net/2021/09/10/4h2dYykSWixXJcj.png)
说了数据结构中的数据那数据结构中的结构又是什么呢?
数据结构
结构,简单的理解就是关系,比如分子结构,就是说组成分子的原子之间的排列方式。严格点说,结构是指各个组成部分相互搭配和排列的方式。在现实世界中,不同数据元素之间不是独立的,而是存在特定的关系,我们将这些关系称为结构。那数据结构是什么?
数据结构(Data Structure)是相互之间存在一种或多种特定关系的数据元素的集合。换句话说,数据结构是带 " 结构"的数据元素的集合,“结构”就是指数据元素之间存在的关系。
逻辑结构和物理结构
逻辑结构
数据的逻辑结构是从逻辑关系上描述数据,它与数据的存储无关,是独立于计算机的。因此,数据的逻辑结构可以看作是从具体问题抽象出来的数学模型。数据的逻辑结构有两个要素:一是数据元素;二是关系。
![](https://i.loli.net/2021/09/10/d6u8bfw7ZUTRDIS.png)
![](https://i.loli.net/2021/09/10/mxA1lwEhqGnNFgz.png)
物理结构/存储结构
物理结构:数据的逻辑结构在计算机中(内存)的存储形式。分为顺序存储结构、链式存储结构、索引存储结构、散列存储结构。
1.顺序存储结构
顺序存储结构是把数据元素存放在连续的存储单元里,数据元素之间的逻辑关系是通过数据元素的位置。(在前面的数据元素就存在前面;在后面的数据元素就存在后面)C语言用数组来实现顺序存储结构
![](https://i.loli.net/2021/09/10/yV92JNiHet6a1zD.png)
用一组任意的存储单元存储数据元素(可能连续也可能不连续),数据元素之间的逻辑关系用指针来表示(用指针存放后继元素的存储地址)
![](https://i.loli.net/2021/09/10/Alq8ai3IYgCX14V.png)
3.索引存储结构
在存储节点信息的同时,还建立附加索引
索引表中的每一项称为一个索引项,
索引项的一般形式是:(关键字,地址)
关键字是能唯一标识一个结点的那些数据项。
![](https://i.loli.net/2021/09/10/Xz2GKAtOFM4I7Zr.png)
![](https://i.loli.net/2021/09/10/gHZFdKm1NPAY2EW.png)
数据类型
说到数据类型其实我们并不陌生,在使用高级程序设计语言编写程序时,必须对程序中出现的每个变量、常量或表达式、C语言中函数的参数、返回值,明确说明它们所属的数据类型。
C语言中:提供int,char,float,double等基本数据类型;数组、结构、共用体、枚举等构造数据类型;还有指针、空(void)类型,用户也可用typedef自己定义数据类型。而另一些常用的数据结构,如栈、队列、树、图等,不能直接用数据类型来表示。
在C语言中,数据类型可以分为两类:
原子类型:是不可以再分解的基本类型,包括整型、实型、字符型等
结构类型:由若干个类型组合而成,是可以再分解的。例如,整型姿型数据组成的数组。
当年那些设计计算机语言的人,为什么会考虑到数据类型呢?
比如,大家都需要住房子,也都希望房子越大越好。但显然,没有钱,考虑房子是没啥意义的。于是商品房就出现了各种各样的房型,有别墅的,有错层的,有单间的;有一百多平米的,也有几十平米的,甚至在北京还出现了胶囊公寓——只有两平米的房间……这样就满足了不同人的需要。
类型明显或隐含地规定了程序执行期间变量或表达式的取值范围、存储方式以及允许进行的运算。
例如,C语言中定义变量i为int类型,就表示是[min,max]范围的整数,[-32768~32767,16位计算机上]
在这个整数集上可以进行+、-、*、/、%的操作,而不能进行其他数据类型比如字符串的一些操作,而实型变量也有自己的取值范围和相应运算,比如取模运算是不能用于实型变量的。
数据类型是一个值的集合和定义在这个值集上的一组操作的总称。
抽象
你在这幅图中看到了什么?相信大多数人会说看到的圆,其实这就是抽象。我们看到了他的本质,而去掉了一些非本质的东西,比如大小颜色,线条的粗细,空心还是实心。
那什么是圆呢?
圆是到某个点距离相等的点的集合这个定点就是圆心,距离就是半径,我们就可以描述这个圆的一些相关信息了
运算:构造圆、求面积、求周长
抽象数据类型
![](https://i.loli.net/2021/09/10/x5SWdJPRabNFiue.png)
抽象数据类型的定义形式
![](https://i.loli.net/2021/09/10/7K4vmVeo6hgakWR.png)
![](https://i.loli.net/2021/09/10/CSJojLIpDeHZvnx.png)
![](https://i.loli.net/2021/09/10/cqNCW2SrzXJGiTk.png)
![](https://i.loli.net/2021/09/10/1HChefLI693spqo.png)
总结
![](https://i.loli.net/2021/09/10/AhCPbvMJ5l4GFpr.png)
渐进时间复杂度
渐进时间复杂度也称时间复杂度,它表示的是代码的执行时间随着数据规模增长的变化程度,注意,它指的不是代码具体的执行时间。时间复杂度通过大O复杂度表示法来表示,下面通过两段代码来表示。
public int cal(int n){
int sum = 0;
for(int i = 0;i < n; i++){
sum+=i; //求和
}
return sum;
}
在上面这段代码中,每一行代码对应的执行时间实际上应该都是不一样的,但是我们是粗略估计,我们假设每一行代码的执行时间都是一样的为unit_time,考虑每行代码的执行次数,在这个假设下代码的执行时间应为(2n+2)*unit_time。
public int cal(int n){
int sum=0;
for(int i=0;i < n;i++){
for(int j=0;j < n;j++){
sum = sum + i * j;
}
}
return sum;
}
再来看一下这段代码,根据上面的分析方法,这段代码的总执行次数应该为2n²+n+2,执行时间为(2n²+n+2)*unit_time。
通过这两段代码我们可以知道,代码的执行时间应该**与代码的总执行次数成正比。**总结为公式就是:
T(n)=O(f(n))
其中T(n)表示代码的执行时间,n表示数据规模的大小,f(n)表示代码的执行次数总和,O表示代码的执行时间T(n)与f(n)成正比。这就是大O复杂度表示法,用于表示时间复杂度。
![](https://i.loli.net/2021/09/13/xIHd12qFn4Jkap9.png)
如果是m次的多项式
![](https://i.loli.net/2021/09/13/Bde36UTL8At9Dlc.png)
计算时间复杂度的方法
![](https://i.loli.net/2021/09/13/tZLHN3K2OWhybGp.png)
记住这句话,时间复杂度是由嵌套最深层语句的频度决定的
如果是复杂的算法,可以分成几个容易估算的部分
![](https://i.loli.net/2021/09/13/NZi8SAXwCPYmzdv.png)
不懂可参考 https://blog.csdn.net/zihonggege/article/details/104527411/
算法定义
解决问题的方法和步骤。在计算机中表现为指令的有限序列。其中每条指令表示一个或多个操作。
算法的描述
自然语言;流程图【NS图、框图】;伪代码(类C语言);程序设计(C、Java…)
![](https://i.loli.net/2021/09/10/b7r9GIcmvjfz6CT.png)
![](https://i.loli.net/2021/09/10/cpKNGzoRDJtB3gd.png)
程序与算法
程序=数据结构+算法
数据结构通过算法来实现操作
算法根据数据结构设计程序
算法的特性
![](https://i.loli.net/2021/09/10/4evKDxQ1ZqozUMT.png)
算法的设计要求
好的算法应该具有正确性、可读性、健壮性、时间效率高和存储量低的特征。
1.正确性(Correctness):能正确的反映问题的需求,能得到正确的答案。
2.可 读性(Readability):算法,首先应便于人们理解和相互交流,其次才是机器可执行性。可读性强的算法有助于人们对算法的理解,而难懂的算法易于隐藏错误,且难于调试和修改。
3.健壮性(Robustness):当输入的数据非法时,好的算法能适当地做出正确反应或进行相应处理,而不会产生一些莫名其妙的输出结果。
4.(高效性)时间效率高和存储量低
算法分析
算法分析的目的是看算法实际是否可行,并在同一问题存在多个算法时可进行性能上的比较,以便从中挑选出比较优的算法。
(时间效率)运行时间的长短和(空间效率)占用内存空间的大小是衡量算法好坏的重要因素。
衡量算法时间效率的方法主要有两类:事后统计法和事前分析估算法。
事后统计法需要先将算法实现,然后测算其时间和空间开销。这种方法的缺陷很显然,一是必须把算法转换成可执行的程序,如果编辑出来发现它根本是很糟的算法,不是竹篮打水一场空吗?二是时空开销的测算结果依赖于计算机的软硬件等环境因素,这容易掩盖算法本身的优劣。三是算法的测试数据设计困难,并且程序的运行时间往往还与测试数据的规模有很大关系,效率高的算法在小的测试数据面前往往得不到体现。 比如10个数字的排序,不管用什么算法,差异几乎是零。而如果有一百万个随机数字排序,那不同算法的差异就非常大了。那么我们为了比较算法,到底用多少数据来测试,这是很难判断的问题。所以我们通常采用事前分析估算法。
一个算法的执行时间大致上等于其所有语句执行时间的总和,而语句的执行时间则为该条语句的重复执行次数和执行一次所需时间的乘积。一条语句的重复执行次数称作语句频度(FrequencyCount)。语句的执行要由源程序经编译程序翻译成目标代码,目标代码经装配再执行,因此语句执行一次实际所需的具体时间是与机器的软、硬件环境(如机器速度、编译程序质量等)密切相关的。设每条语句执行一次所需的时间均是单位时间,则一个算法的执行时间可用该算法中所有语句频度之和来度量。所谓的算法分析并非精确统计算法实际执行所需时间,而是针对算法中语句的执行次数做出估计,从中得到算法执行时间的信息。
![](https://i.loli.net/2021/09/10/hb6DMdjpPo4kCzO.png)