1、理解什么是线性表
2、数组数据结构,ArrayList的部分源码
3、链表数据结构,LinkedList的部分源码
4、栈这种数据结构,stack的部分源码
5、队列这种数据结构
线性结构,比如数组、链表、栈、队列,像一条长线一样的结构,
后续的数、堆、图是非线性数据结构
3.1数组,是一种线性表数据结构,一组连续内存空间,来存储一组具有相同类型的数据
3.2数组的逻辑结构和物理结构,分别是什么,逻辑结构指的是可以用什么的表示方式来描述数组元素。物理结构是指的数组元素实际存储形式。
3.2.1数组元素的访问
3.2.2数组下标为什么是0开始
从数组存储的内存模型上来看,下标最确切的定义应该是 偏移 ,如果用array来表示数组的首地址,array[0]就是偏移为0的位置,也就是首地址,array[k]就表示偏移k个,type_size的位置,所以计算array[k]的内存地址公式是,array[k]_address=base_address+k*type_size。如果是下标从1开始,那么计算array[k]的内存地址就会变成
array[k]_address=base_address+(k-1)*type_size
3.3数组的特点,这个特点,是因为数组的特性导致的,特性是数组是一块连续的内存空间,来存储相同类型的数据
3.3.1高效的随机访问
数组元素的访问是通过下标访问的,计算机通过数组的首地址和寻址公式,就很快速的找到想要访问的元素
3.3.2低效插入和删除
数组是一段连续的内存空间,因此为了保证数组的连续性会使得数组的插入和删除的效率变的很低
插入,假设数组的长度为 n,现在如果我们需要将一个数据插入到数组中的第 k 个位置。为了把第 k 个位置腾出来给新来的数据,我们需要将第 k~n 这部分的元素都顺序地往后挪一位。
那数组插入有没有相对优化的方案呢?
如果数组中的数据是有序的,我们在某个位置插入一个新的元素时,就必须按照刚才的方法搬移 k之后的数据。但是,如果数组中存储的数据并没有任何规律,数组只是被当作一个存储数据的集合。在这种情况下,如果要将某个数组插入到第 k个位置,为了避免大规模的数据搬移,我们还有一个简单的办法就是,直接将第 k 位的数据搬移到数组元素的最后,把新的元素直接放入第 k 个位置。这种处理思想会在快排中用到。
删除,例子,假如一个数组里有6个元素,a1 a2 a3 a4 a5 a6,依次删除a1 a2,这两个元素,为了避免 a3 a4 a5 a6这几个数据被移动两次,我们可以先记录下来已经删除的数据,每次删除操作并不是真正的移出数据,只是记录被删除的数据,当数据没有更多的存储空间的时候,这个时候在触发一次真正的删除,这样就大大减少了删除数据导致的数据移动次数。这个就像是jvm里垃圾回收算法,第一种算法,标记清除算法,先标记下来删除的元素,后期没有空间了再触发一次真正删除。
3.4数组的应用
针对数组类型,很多语言提供了容器类,比如java中的ArrayList。ArrayList最大的优势是将很多数组操作的细节封装起来,比如前面提到的,数组的插入、删除数据需要搬移其他的数据,还有一个优势就是支持动态扩容,数组本身在定义的时候需要预先指定大小,因为需要分配连续的内存空间,如果我们申请了10个数组,当第11个数据需要存储到数组时我们就要重新分配一块更大 的空间,将原来的数据复制过去,然后再将新的数据插入。如果用ArrayList,我们不用关心底层的逻辑,每次存储空间不够的时候,它会自动的将空间扩容到1.5倍大小,不过,扩容操作和内存申请数据搬移,是比较耗时的,所以,如果事先能确定需要存储的数据大小,最好在创建ArrayList的时候事先指定数据大小。
3.4.1 ArrayList源码分析,ArrayList底层是采用的数组来进行数据的存储的,其次ArrayList提供了相关的方法来对底层数组中的元素进行操作。
3.4.2 容器构建及添加元素
3.4.3 获取元素
源码分析:
4.链表
4.1概念,链表是一种物理储存单元上非连续、非顺序的储存结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
4.2存储结构,相比数组,链表是稍微复杂一点的数据结构,从底层存储结构来看:数组,需要一块连续的内存空间,对内存的要求比较高,比如我们申请1000M的数组,如果内存中没有连续的足够大的存储空间则会申请失败,即便内存的剩余空间大于1000M,仍然会申请失败。
链表:与数组相反,它并不需要一块连续的内存空间,它通过指针将一组零散的内存块串联起来使用,所以如果我们申请一个1000M大小的链表,只要内存剩余的可用空间大于1000M,就不会有问题。
数组和链表的存储结构对比
此时我们在来回顾我们刚刚提到的链表的概念:物理存储单元上非连续、非顺序,元素的逻辑顺序是通过链表中的指针链接次序实现的,链表由一系列结点组成,每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域
当然了这只是我们提到的链表的最基本的存储形式,其实链表有很多种不同的类型,分别对应了不同的结构,接下来我们就来分析不同的链表类型。
4.3链表类型
单链表:特点插入删除、效率比较高,性能好,因为不需要搬移数据,只需要把地址指向更改位置就可以。但是它的查询速度较慢,需要遍历所有节点。把第一个结点叫作头结点,把最后一个结点叫作尾结点。其中,头结点用来记录链表的基地址,有了它,我们就可以遍历得到整条链表。而尾结点特殊的地方是:指针不是指向下一个结点,而是指向一个空地址 NULL,表示这是链表上最后一个结点。
循环链表
循环链表是一种特殊的单链表,它跟单链表唯一的区别就在尾结点。我们知道,单链表的尾结点指针指向空地址,表示这就是最后的结点了。而循环链表的尾结点指针是指向链表的头结点。
双向链表LinkedHashMap 底层就是用的双向链表,它支持两个方向,每个结点不止有一个后继指针 next 指向后面的结点,还有一个前驱指针 prev 指向前面的结点,双向链表要比单链表占用更多的内存空间。虽然两个指针比较浪费存储空间,但可以支持双向遍历,这样也带来了双向链表操作的灵活性。
双向循环链表,
4.4链表和数组性能比较
数组在内存空间是连续的,确定点是大小是固定的,所以查询比较快,但是增删需要搬移数据,所以增删比较慢。数组一开始分配给他的内存空间是一块固定的大小。
链表在内存空间是离散度,并且支持扩容,它是通过指针串联的每个节点,所以他的增删比较快,但是它的查询慢,要遍历所有节点。
4.5链表的应用,
LinkedList,源码分析,添加元素的时候,分析;获取元素时候的分析
第一、查找使用了二分查找思想、第二、双向链表既可以往前查,也可以往后查;第三链表的特性。
习题:
LinkedList和ArrayList的区别;
1:ArrayList的实现基于数组,LinkedList的实现基于双向链表
2:对于随机访问,ArrayList优于LinkedList,ArrayList可以根据下标对元素进行随机访问。而LinkedList的每一个元素都依靠地址指针和它后一个元素连接在一起,在这种情况下,查找某个元素只能从链表头开始查询直到找到为止
3:对于插入和删除操作,LinkedList优于ArrayList,因为当元素被添加到LinkedList任意位置的时候,不需要像ArrayList那样重新计算大小或者是更新索引。
4:LinkedList比ArrayList更占内存,因为LinkedList的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。
反转单链表。
5、栈
5.1栈的概念
关于“栈”这种数据结构,它有一个典型的特点:先进后出,后进先出;只要满足这种特点的数据结构我们就可以说这是典型的“栈”数据结构,我们一般将这个特点归纳为一个:后进先出,英文表示为:Last In First Out即 LIFO。为了更好的理解栈这种数据结构,我们以一幅图的形式来表示,如下:
5.2栈的实现:
基于数组顺序栈的实现
支持动态扩容的顺序栈
基于链表的链式栈的实现
5.3栈的应用
队列
总结:
数组:
数组是一种线性表数据结构,它用一组连续的内存空间,来存储一组具有相同类型的数据。数组通过下标来访问元素,数组访问时可以通过寻址公式快速定位元素位置,但是插入和删除操作为了保证数组的连续性要进行很多数据的搬移操作
链表
链表(Linked list)是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。链表在进行元素的插入和删除操作时的效率高,但是链表在进行元素获取时的效率低,而链表又分为单链表,双向链表,循环链表,双向循环链表等
栈
栈满足先进后出,后进先出即LIFO,属于一种操作受限的线性表,只允许在一端进行元素的插入和删除,分别是入栈和出栈,时间复杂度都是O(1),栈可以基于数组实现也可以基于链表实现,分别是顺序栈和链式栈。
队列
队列满足先进先出的特点即FIFO,队列跟栈一样都属于操作受限的线性表,只不过队列是只允许在队列头获取元素,在队列尾插入元素,分别叫入队列和出队列,当然队列也可以基于数组和链表来实现,分别叫顺序队列和链式队列,在队列中还有一种循环队列可以解决顺序队列需要频繁搬移数据的问题。
1:ArrayList的源码
2:LinkedList的源码
3:Stack的源码
4:单链表的反转
5:自主实现基于数组且支持动态扩容的顺序栈
6:自主实现基于链表的链式栈
7:自主实现基于数组的顺序队列
8:自主实现基于链表的链式队列
9:自主实现一个基于数组的循环队列