抽象数据类型(ADT):抽象数据类型(ADT)是一个实现包括储存数据元素的存储结构以及实现基本操作的算法。使得只研究和使用它的结构而不用考虑它的实现细节成为可能。比如我们使用List、Map、Set等等只需要了解它的api和性质功能即可。而具体的实现可能是不同的方案,比如List的实现有数组和链表不同选择。
三要素逻辑结构:数据元素之间的逻辑关系。逻辑结构分为线性结构和非线性结构。线性结构就是顺序表、链表之类。而非线性就是集合、树、图这些结构。
存储结构:数据结构在计算机中的表示(又称映像,也称物理结构),存储结构主要分为顺序存储、链式存储、索引存储和散列(哈希)存储,这几种存储通过下面这张图简单了解一下(仅仅为理解不考虑更多):
数据的运算:施加在数据上的运算包括运算的定义和实现,运算的定义基于逻辑结构,运算的实现基于存储结构。
在这里容易混淆的是逻辑结构与存储结构的概念。对于逻辑结构,不难看得出逻辑二字,逻辑关系也就是两者存在数据上的关系而不考虑物理地址的关系,比如线性结构和非线性结构,它描述的是一组数据中联系的方式和形式,他针对的是数据。看中的是数据结构的功能,比如线性表就是前后有序的,我需要一个有序的集合就可以使用线性表。
而存储结构就是跟物理地址挂钩的。因为同样逻辑结构采用不同存储结构实现适用场景和性能可能不同。比如同样是线性表,可能有多种存储结构的实现方式。比如顺序表和链表(Arraylist,Linkedlist)它们的存储结构就不同,一个是顺序存储(数组)实现,一个是链式存储(链表)实现。它关注的是计算机运行物理地址的关系。但通常同一类存储结构实现的一些数据结构有一些类似的共同点和缺点(线性易查难插、链式易插难查等等)。
算法分析上面讲了数据结构相关概念,下面对算法分析的一些概念进行描述。
算法的五个重要特征:有穷性、确定性、可行性、输入、输出。这些从字面意思即可理解,其中有穷性强调算法要有结束的时候不能无限循环;而确定性是每条指令有它意义,相同的输入得到相同的输出;可行性是指算法每个步骤经过若干次执行可以实现;输入是0个或多个输入(可0);输出是1个或多个输出(一定要有输出)。
而一个好的算法,通常更要着重考虑的是效率和空间资源占用(时间复杂度和空间复杂度),通常复杂度更多描述的是一个量级程度而很少用具体数字描述。
空间复杂度概念:是对一个算法在运行过程中临时占用存储空间大小的量度,记做S(n)=O(f(n))
空间复杂度其实在算法的衡量占比是比较低的(我们经常使用牺牲空间换时间的数据结构和算法),但是不能忽视空间复杂度中重要性。无论在刷题还是实际项目生产内存都是一个极大额指标。对于Java而言更是如此。本身内存就大,如果采用的存储逻辑不太好会占用更多的系统资源,对服务造成压力。
而算法很多情况都是牺牲空间换取时间(效率)。就比如我们熟知的字符串匹配String.contains()方法,我们都知道他是暴力破解,时间复杂度为O(n^2),不需要借助额外内存。而KMP算法在效率和速度上都原生暴力方法,但是KMP要借助其他数组(next[])进行标记储存运算。就用到了空间开销。再比如归并排序也会借助新数组在递归分冶的适合进行逐级计算,提高效率,但增加点影响不大的内存开销。
当然,算法的空间花销最大不能超过jvm设置的最大值,一般为2G.(2147483645)如果开二维数组多种多维数据不要开的太大,可能会导致heap OutOfMemoryError。
时间复杂度概念:计算机科学中,算法的时间复杂度是一个函数,它定性描述了该算法的运行时间。这是一个关于代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是渐近的,它考察当输入值大小趋近无穷时的情况。
时间复杂度的排序:O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) <O(n!) < O(n^n)
常见时间复杂度:对于时间复杂度,很多人的概念是比较模糊的。下面举例子说明一些时间复杂度。
O(1): 常数函数
a=15
O(logn): 对数函数
for(int i=1;i<n;i*=2)
分析:假设执行t次使得i=n;有2^t=n; t=log2~n,为log级别时间复杂度为O(logn)。
还有典型的二分查找,拓展欧几里得,快速幂等算法均为O(logn)。属于高效率算法。
O(n): 线性函数
for (int i=0;i<n;i++)
比较常见,能够良好解决大部分问题。
O(nlogn):
for (int i=1;i<n;i++)
for (int j=1;j<i;j*=2)
常见的排序算法很多正常情况都是nlogn,比如快排、归并排序。这种算法效率大部分也还不错。
O(n^2)
for(int i=0;i<n;i++)
for(int j=0;j<i;j++)
其实O(n2)的效率就不敢恭维了。对于大的数据O(n2)甚至更高次方的执行效果会很差。