数据结构:理解成如何有效地存储数据? 顺序存储还是链式存储……
(一)逻辑结构&物理结构
存储数据本身,也就是将数据存储到内存里。数据在内存中的存储状态,就称为数据的存储结构,也叫物理结构。
数据结构中,将数据之间的关系称为数据的逻辑结构。以下图所示的家谱图为例,数据之间存在很多关系,比如张亮是张平的父辈、是张静的祖辈等,所有这些关系就构成了数据的逻辑结构。
(二) 数据的存储结构
数据结构在计算机中的表示(又称映像)称为数据的物理结构,又称存储结构。
由于数据元素之间关系在计算机中有两种不同的表示方法:顺序映像和非顺序映像,并由此得到不同的存储结构:顺序存储结构和链式存储结构。
两者的区别:
链式存储结构的内存地址不一定是连续的,但顺序存储结构的内存地址一定是连续的;
链式存储适用于在较频繁地插入、删除、更新元素时,而顺序存储结构适用于频繁查询时使用。因为顺序表可以随机存取,也可以顺序存取,链表是顺序存取的。
当插入/删除时移动元素个数时,顺序表平均需要移动近一半元素,链表不需要移动元素,只需要修改指针。
链表的存储空间是动态分配的,而顺序表的存储空间是静态分配的。
存储密度 = 结点数据本身所占的存储量/结点结构所占的存储总量。
顺序表的存储密度 = 1
链表的存储密度 < 1
优缺点:
顺序存储时,相邻数据元素的存放地址也相邻(逻辑与物理统一);要求内存中可用存储单元的地址必须是连续的。
优点:存储密度大(=1),存储空间利用率高。缺点:插入或删除元素时不方便。
链式存储时,相邻数据元素可随意存放,但所占存储空间分两部分,一部分存放结点值,另一部分存放表示结点间关系的指针 优点:插入或删除元素时很方便,使用灵活。缺点:存储密度小(<1),存储空间利用率低。
实际推荐使用情况:
顺序表适宜于做查找这样的静态操作;链表宜于做插入、删除这样的动态操作。
若线性表的长度变化不大,且其主要操作是查找,则采用顺序表;
若线性表的长度变化较大,且其主要操作是插入、删除操作,则采用链表。
(三)逻辑结构
数据结构(data structure)是相互之间存在一种或多种特定关系的数据元素的集合。
根据数据元素之间的关系,通常有以下基本结构:
①集合:结构中数据元素之间除了“同属于一个集合”的关系外,并无其他关系。
生活例子: 一个班级里的学生, 一张嘴里的 26 颗牙齿。
②线性结构: 结构中数据元素之间存在一对一的关系。
例子:排队,队列,字符串;
③树形结构:结构中数据元素之间存在一对多的关系。
例子:族谱,树;
④图形结构:结构中数据元素之间存在多对多的关系。
例子:中国铁路图,某市城市规划图;
总结:
对于每一种逻辑结构的数据,数据结构都提供了存储它们的方案:
- 查找表存储结构:专门存储无逻辑结构的数据;
- 线性存储结构:专门存储具有“一对一”逻辑结构的数据;
- 树存储结构:专门存储具有“一对多”逻辑结构的数据;
- 图存储结构:专门用来存储具有“多对多”关系的数据;
数据的物理结构有 2 种,逻辑结构有 4 种,它们可以随意组合。例如,无关系的数据可以选择集中存放,也可以选择分散存放。针对具有不同物理结构和逻辑结构的数据,数据结构都会给出最恰当的存储方案。学习数据结构,实际上就是学习这些存储数据的方案。
如下图所示:
(四)时间复杂度和空间复杂度
Ⅰ 时间复杂度
同一问题可用不同算法解决,而一个算法的质量优劣将影响到算法乃至程序的效率。算法分析的目的在于选择合适算法和改进算法。所谓算法,简单理解就是解决问题的方法(方案、思路)。通常情况下,一个问题的解决方法会有很多种,或者说解决这个问题的算法有很多种。
计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。这是一个关于代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是渐近的,它考察当输入值大小趋近无穷时的情况。
算法复杂度分为时间复杂度和空间复杂度。 算法的复杂性体现在运行该算法时的计算机所需资源的多少上,计算机资源最重要的是时间和空间(即寄存器)资源,因此复杂度分为时间和空间复杂度 。其作用:时间复杂度是指执行算法所需要的计算工作量 用来预估算法的执行时间 ;而空间复杂度是指执行这个算法所需要的内存空间。 用来估算一个算法执行时占用的内存大小
计算方法
1.一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
随着模块n的增大,算法执行的时间的增长率和 f(n) 的增长率成正比,所以 f(n) 越小,算法的时间复杂度越低,算法的效率越高。
2. 在计算时间复杂度的时候,先找出算法的基本操作,然后根据相应的各语句确定它的执行次数,再找出 T(n) 的同数量级(它的同数量级有以下:1,log2n,n,n log2n ,n₂,n³,2ⁿ,n!),找出后,f(n) = 该数量级,若 T(n)/f(n) 求极限可得到一常数c,则时间复杂度T(n) = O(f(n))。
3.时间复杂度比较简单的计算方法是:看看有几重for循环,只有一重则时间复杂度为O(n),二重则为O(n²),依此类推,如果有二分则为O(logn),二分例如快速幂、二分查找,如果一个for循环套一个二分,那么时间复杂度则为O(nlogn)。
Ⅱ 空间复杂度
空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度,记做S(n)=O(f(n))。一般的递归算法就要有O(n)的空间复杂度了,因为每次递归都要存储返回信息。
空间复杂度也习惯用大 O 记法表示。空间复杂度的估算方法是:
- 如果算法中额外申请的内存空间不受用户输入值的影响(是一个固定值),那么该算法的空间复杂度用
O(1)
表示; - 如果随着输入值 n 的增大,算法申请的存储空间成线性增长,则程序的空间复杂度用
O(n)
表示; - 如果随着输入值 n 的增大,程序申请的存储空间成 n2 关系增长,则程序的空间复杂度用
O(n2)
表示; - 如果随着输入值 n 的增大,程序申请的存储空间成 n3 关系增长,则程序的空间复杂度用
O(n3)
表示;
一个算法的优劣主要从算法的执行时间和所需要占用的存储空间两个方面衡量。 有得有失,投其所好 看你要什么,快(忍受大内存)OR 慢(内存小)
在实际生活中,挑选 "好" 算法往往更注重的是时间复杂度,空间复杂度只要处于一个合理的范围即可。