🌈个人主页:小新_-
🎈个人座右铭:“成功者不是从不失败的人,而是从不放弃的人!”🎈
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝
✨让小新带着你快乐的学习吧~✨
目录
一、数据结构的起源
在20世纪60年代末至70年代初,随着大型程序的出现,软件开始相对独立,结构程序设计成为了程序设计方法学的主要内容。人们开始越来越重视数据结构,认为程序设计的实质是对确定的问题选择一种好的结构,加上设计一种好的算法。因此,从20世纪70年代中期到80年代初,各种版本的数据结构著作相继出现
数据结构的起源可以追溯到1968年,当时美国唐·欧·克努特教授开创了数据结构的最初体系,他所著的《计算机程序设计技巧》第一卷《基本算法》(计算机中的《圣经》)是第一本较系统地阐述数据的逻辑结构和存储结构及其操作的著作
数据结构是一门研究非数值计算的程序设计问题中的操作对象,以及他们之间关系和操作的学科
数据结构发展经历了三个阶段:
- 无结构阶段
- 结构化阶段
- 面向对象阶段
1)无结构阶段
40~60年代,计算机主要用于科学计算,程序设计技术以机器语言和汇编语言为主,数据之间的关系主要依赖于数学公式或数学模型,此时数据结构概念并未明确形成。
2)结构化阶段
60~80年代,计算机开始广泛应用于非数值处理领域,数据表示成为程序设计的重要问题,人们认识到了程序设计规范化的必要性,提出了程序结构模块化,并开始注意数据表示与操作的结构化。数据结构及抽象数据类型就是在这种情况下形成的,随着数据规模的加大,程序的设计越来越依赖于数据结构的设计,此时数据结构开始广泛普及。
此间也有非常多的数据结构相关的文献产出,最为著名的是图灵奖获得者沃斯的一个著名公式:
程序=数据结构+算法
3)面向对象阶段
面向对象阶段的开始是在80年代初期,随着计算机不断普及,计算机性能以及需求不断增加,面向对象的程序设计被逐步提出,在对象的世界中,程序设计中大大减少了重复设计的部分,数据结构在这个阶段逐渐变得丰富,大量的封装类出现,减少了程序设计者的负担,数据结构因此变得更加友好
二、基本概念和术语
1、数据
数据:是描述客观事实的数、字符以及所有能输入计算机并被计算机程序处理的符号的集合。它是计算机操作的对象的总称,也是计算机处理的信息的某种特定的符号表示形式 。数据不仅仅包括整型、实型等数据类型,也包括字符以及声音、图片、视频等非数值类型
2、数据元素
数据元素:数据元素是数据的基本单位,它是由数据项组成的。
比如:人类中什么是数据元素?那当然是人啊;再比如,畜生中什么是数据元素,那当然是猪,牛、羊,鸡、鸭、鹅等
3、数据项
数据项:数据项是数据的最小单位,它是由一组属性来描述、定义、标识、表示和允许值的一个数据单元。比如,人这个数据是由眼睛,鼻子,嘴巴,胳膊等这些数据项组成,还可以是家庭地址,身份证号码,电话号码等这些数据项组成。具体有那些数据项,根据需求决定。
数据项是数据不可分割的最小单位!!!
struct student {
char name[20];char num[20];
int age;
char class[20];
};
struct student stu1, stu2;//stu1,stu2就是数据
stu1.name = "zhangsan";//这就是数据项
stu2.age = 20;//这就是数据项
4、数据对象
数据对象:是性质相同(即为数据元素具有相同数量和类别的数据项)的数据元素的集合,是数据的子集
比如:上面代码中的stu1和stu2都有自己的学号,班级,年龄等数据项
5、数据结构
结构,简单的理解就是关系。比如:分子结构就是说的分子与原子的排列方式
在现实生活中,不同数据元素不是独立的,而是存在特定的关系,我们将这种关系称之为结构。拿数据结构是什么呢?
数据结构:相互之间存在一种或者多种关系数据元素的集合
为了编写一个“好的”程序,必须分析待处理对象的特性以及各处理对象之间的关系,这就是研究数据结构的意义
三、逻辑结构与存储结构
按照视点的不同,我们将数据结构分为逻辑结构和物理结构
1、逻辑结构
逻辑结构:是指数据对象中数据元素之间的相互关系。(今后需要特别关注),逻辑结构又分为以下四种:
1)集合结构
集合结构:集合结构中的数据元素除了同属于一个集合外,他们之间没有其他的关系。各个数据元素是“平等”的,他们的共同属性是同属于一个集合。
数据结构中的集合关系类似于数学中的集合关系,如图:
2)线性关系
线性结构:各个数据元素是一对一的关系,如图所示
3)树形结构
树形结构:树形结构重点数据元素存在一对多的层次关系,如图所示:
实际应用:书的目录结构
4)图形结构
图形结构:图形结构中的数据元素是多对多的关系,如图所示:
从之前列子当中也可以看出逻辑结构是针对具体问题的,为了研究和解决某一个问题,选择一个合适的数据结构来表示数据元素之间的逻辑关系
2、存储结构
数据的存储结构,也叫做物理结构
存储结构:是指数据的逻辑结构在计算机中的存储形式(俩种:顺序存储和链式存储)。
1)顺序存储
顺序存储结构:把数据元素放在地址连续的存储单元里,其数据的逻辑关系和物理关系是一致的。可以理解伪数组,如图:
2)链式存储
链式存储结构:把数据元素放在任意的存储单元,这些存储单元可以连续亦或是不。
数据元素的存储关系并不能反映其逻辑关系,需要用一个空间来存储数据元素的地址,这样就可以通过地址来找到相关数据元素的位置。如图所示:
3)索引存储
索引存储结构:它通过建立一个索引表来维护数据元素的地址信息,使得数据元素可以根据关键字进行快速查找。如图所示:
4)散列存储(哈希存储)
散列存储(Hash):旨在将数据元素的存储位置与关键码之间建立确定对应关系,以便快速查找数据元素
散列存储的基本思想是:由节点的关键码值决定节点的存储地址。这种技术不仅可用于查找,还可用于存储
四、算法
1、算法的定义
算法是计算机科学中的一个核心概念,它是一系列解决问题的步骤或方法,用于解决特定问题位置执行特定的任务。通常有以下特性:
- 确定性:算法中的每一步骤都必须有确定的含义,即每一步都应该能够被准确执行。这意味着算法的每个步骤必须是精确无误的,以确保得到正确的结果
- 有限性:算法必须在有限步骤内结束,不能出现无限循环或死循环的情况。也就是说,算法需要有明确的终止条件
- 可行性:算法必须能够在有限时间内完成。也就是说,算法的执行时间应该是可接受的,并且能够处理给定的输入大小
- 输入输出:一个算法有零个或多个输入,这些输入取自于某个特定的对象的集合。一个算法有一个或多个输出,这些输出是同输入有着某种特定关系的量
- 可读性:算法应当具有良好的可读性,以助于人们理解
- 优化:算法应该尽可能高效,使用最小的资源(时间和空间)来完成任务
2、算法设计要求
- 正确性:算法必须能够正确地解决问题,对于给定的输入,算法应产生预期的输出 。
- 可读性:算法应该清晰易读,便于其他开发者理解和维护
- 健壮性:算法应该能够处理非法或不合理的输入,并在必要时给出适当的错误处理
- 时间效率高和存储量低:算法应该尽可能高效,包括时间效率和空间效率。时间效率指的是算法执行所需的时间,空间效率则是算法执行所需的存储空间 。
3、算法效率的度量方法
1.事后统计方法
事后统计方法:这种方法主要是通过设计好的测试程序和数据,利用计算机计时器对不同算法编制的程序的运行时间进行比较,从而确定算法效率的高低。
但这种方法显然是有很大缺陷的:必须依据算法事先编制好程序,这通常需要花费大量的时间和精力。时间的比较依赖计算机硬件和软件等环境因素,有时会掩盖算法本身的优劣。
基于事后统计方法有这样那样的缺陷,我们考虑不予采纳。
2.事前分析估算方法
我们的计算机前辈们,为了对算法的评判更科学,研究出了一种叫做事前分析估算的方法。
事前分析估算方法:在计算机程序编制前,依据统计方法对算法进行估算。
经过分析,我们发现,一个用高级程序语言编写的程序在计算机上运行时所消耗的时间取决于下列因素:
1.算法采用的策略、方法。
2.编译产生的代码质量。
3.问题的输入规模。
4.机器执行指令的速度。
第1条当然是算法好坏的根本,第2条要由软件来支持,第4条要看硬件性能。也就是说,抛开这些与计算机硬件、软件有关的因素,一个程序的运行时间,依赖于算法的好坏和问题的输入规模。所谓问题输入规模是指输入量的多少。
列如求和的算法
第一种算法:
int i,sum=0,n=100; /*执行1次 */
for(i=1;i<=n;i++)
{
sum=sum+i;/*执行n次*/
}
printf("%d",sum);/*执行1次*/
第二种算法:
int sum=0,n=100;/*执行1次*/
sum=(1+n)*n/2;/*执行1次*/
printf("%d",sum); /* 执行1次 */
显然,第一种算法,执行了1+n+n+1次=2n+2次;而第二种算法,是1+1+1=3次。
事实上两个算法的第一条和最后一条语句是一样的,所以我们关注的代码其实是中间的那部分,我们把循环看作一个整体,忽略头尾循环判断的开销,那么这两个算法其实就是n次与1次的差距。
因此第二种算法更好。
4、算法时间复杂度
1.算法时间复杂度定义
在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。算法的时间复杂度,也就是算法的时间度量,记作:T(n)=O(f(n))。
它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐近时间复杂度,简称为时间复杂度。其中f(n)是问题规模n的某个函数。这样用大写O()来体现算法时间复杂度的记法,我们称之为大O记法。
一般情况下,随着n的增大,T(n)增长最慢的算法为最优算法。
2.时间复杂度推导方法
那么如何分析一个算法的时间复杂度呢?即如何推导大O阶呢?我们给出了下面的推导方法。
推导大O阶:
1.用常数1取代运行时间中的所有加法常数
2.在修改后的运行次数函数中,只保留最高阶项
3.如果最高阶项存在且不是1,则去除与这个项相乘的常数。
得到的结果就是大O阶。
3、常见时间复杂度
- 常数阶
首先顺序结构的时间复杂度。下面这个算法,为什么时间复杂度不是O(3),而是O(1)。
这个算法的运行次数函数是f(n)=3。根据我们推导大O阶的方法,第一步就是把常数项3改为1。在保留最高阶项时发现,它根本没有最高阶项,所以这个算法的时间复杂度为O(1)。
2.线性阶
线性阶的循环结构会复杂很多。要确定某个算法的阶次,我们常常需要确定某个特定语句或某个语句运行的次数。因此,我们要分析算法的复杂度,关键就是要分析循环结构的运行情况。
下面这段代码,它的循环的时间复杂度为 O(n),因为循环体中的代码须要执行n次。
3.对数阶
下面的这段代码,时间复杂度又是多少呢?
由于每次count乘以2之后,就距离n更近了一分。也就是说有多少个2相乘后大于n,则会退出循环。由2^n=x得到x=log2n。所以复杂度为O(logn)
4.平方阶
一个嵌套循环
复杂度为O(n^2)
5、立方阶
下面也是一个嵌套循环
复杂度为O(n^3)
下面是几种常见量级的T(n)随n的变化,如图:
5、算法空间复杂度
算法的空间复杂度是指算法在执行过程中所需要的额外空间的大小,通常是指算法需要借助的额外存储空间。算法的空间复杂度通常通过计算算法所需要的额外空间大小,来粗略估计算法的空间开销。计算算法的空间复杂度通常遵循以下公式:
s(n)=o(f(n))
其中,s(n)表示算法所需要的额外存储空间的大小,f(n)表示算法空间复杂度的函数,◎表示量级。
本质上,空间复杂度和时间复杂度的表示方式是一样的,只是分析的角度不同而已。
常见的空间复杂度有:
- O(1)﹣常数复杂度,表示算法所需的额外空间是常数级别的,不随问题规模n的增加而变化。
- O(n)﹣线性复杂度,表示算法所需的额外空间随输入规模n的增加而线性增加,最终达到O(n)的级别。
- O(n^2) ﹣平方复杂度,表示算法所需的额外空间随输入规模n的增加而平方增加,最终达到O(n^2)的级别。
- O(log n) ﹣对数复杂度,表示算法所需的额外空间随输入规模n的增加而对数增加,最终达到O(log n) 的级别。
总结
最后,感谢大家的观看!