1. 线性表
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
2. 顺序表
其实就是一个数组。【增删查改】那为什么还有写一个顺序表,直接用数组就好了嘛?不一样,写到类里面 将来就可以 面向对象了。
2.1 概念及结构
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表一般可以分为:
- 静态顺序表:使用定长数组存储。
- 动态顺序表:使用动态开辟的数组存储。
静态顺序表适用于确定知道需要存多少数据的场景.
静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用.
相比之下动态顺序表更灵活, 根据需要动态的分配空间大小.
2.2 接口实现
我们来实现一个动态顺序表. 以下是需要支持的接口.
这里我们挨个拆解出来:
这是我们顺序表的基本结构。下面我们就把顺序表的功能一个一个拆解出来:
打印数据表:
在 pos 位置新增元素:
判断是否包含某个元素:
给 pos 位置的元素设为 value:
删除第一次出现的关键字key:
2.3 顺序表的问题及思考
顺序表中间/头部的插入删除,时间复杂度为O(N)
增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。
思考: 如何解决以上问题呢?下面给出了链表的结构来看看。
3. 链表
3.1 链表的概念及结构
链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。
实际中链表的结构非常多样,如果按一般来分的话就是四种:
单向链表
双向链表
循环链表
双向循环链表
如果细分的话就有以下情况组合起来就有8种链表结构:
- 单向、双向
- 带头、不带头
- 循环、非循环
这八种分别为:
单向 带头 循环
单向 不带头 循环
单向 带头 非循环
单向 不带头 非循环
双向 带头 循环
双向 不带头 循环
双向 带头 非循环
双向 不带头 非循环
注:上述加粗是我们重点需要学习的!!!
虽然有这么多的链表的结构,但是我们重点掌握两种:
- 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
head:里面存储的就是第一个节点(头节点)的地址
head.next:存储的就是下一个节点的地址
尾结点:它的next域是一个null
- 无头双向链表:在Java的集合框架库中LinkedList底层实现就是无头双向循环链表。
最上面的数字是我们每一个数值自身的地址。
prev:指向前一个元素地址
next:下一个节点地址
data:数据
3.2 链表的实现
3.2.1无头单向非循环链表的实现
上面地址为改结点元素的地址
val:数据域
next:下一个结点的地址
head:里面存储的就是第一个结点(头结点)的地址
head.next:存储的就是下一个结点的地址
无头单向非循环链表实现:
上面是我们链表的初步结构(未给功能赋相关代码,大家可以复制他们到自己的idea中进行练习,答案在下文中) 这里我们将他们一个一个拿出来实现 并实现!
打印链表中所有元素:
查找是否包含关键字key是否在单链表当中:
得到单链表的长度:
头插法(一定要记住 绑定位置时一定要先绑定后面的数据 避免后面数据丢失):
尾插法:
任意位置插入,第一个数据结点为0号下标(插入到index后面一个位置):
注意:单向链表找cur时要-1,但双向链表不用 直接返回cur就好
删除第一次出现关键字为key的结点:
删除所有值为key的结点:
清空链表中所有元素:
3.2.2无头双向非循环链表实现:
上面的地址0x888为该结点的地址
val:数据域
prev:上一个结点地址
next:下一个结点地址
head:头结点 一般指向链表的第一个结点
上面是我们链表的初步结构(未给功能赋相关代码,大家可以复制他们到自己的idea中进行练习,答案在下文中) 这里我们将他们一个一个拿出来实现 并实现!
打印链表:
得到单链表的长度:
查找是否包含关键字key是否在单链表当中:
头插法:
尾插法:
注:第一种方法是先让last等于尾结点 再让他的前驱等于上一个地址 而第二种方法是先使插入的尾结点的前驱等于上一个地址 再使其等于尾结点
删除第一次出现关键字为key的结点:
删除所有值为key的结点:
注:他和remove的区别就是删除完后是不是直接return返回,如果要删除所有的key值则不return,让cur继续往后面走。
任意位置插入,第一个数据节点为0号下标:
思路:先判断 在头位置就头插 在尾位置就尾插 在中间就改变四个位置的值。
注意:单向链表找cur时要-1,但双向链表不用 直接返回cur就好
清空链表:
3.3 链表面试题
3.3.1反转链表:
这里的
cur = this.head;
prev = null;
curNext = cur.next;
3.3.2找到链表的中间结点:
3.3.3输入一个链表 返回该链表中倒数第k个结点
3.3.7 链表的回文结构。
3.3.8 输入两个链表,找出它们的第一个公共结点。
他是一个Y字形
3.3.10 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
4. 顺序表和链表的区别和联系
4.1顺序表和链表的区别
顺序表:一白遮百丑
白:空间连续、支持随机访问
丑:
中间或前面部分的插入删除时间复杂度O(N)
增容的代价比较大。
链表:一(胖黑)毁所有
胖黑:以节点为单位存储,不支持随机访问
所有:
任意位置插入删除时间复杂度为O(1)
没有增容问题,插入一个开辟一个空间。
组织:
1、顺序表底层是一个数组,他是一个逻辑上和物理上都是连续的
2、链表是一个由若干结点组成的一个数据结构,逻辑上是连续的 但是在物理上[内存上]是不一定连续的。
操作:
1、顺序表适合,查找相关的操作,因为可以使用下标,直接获取到某个位置的元素
2、链表适合于,频繁的插入和删除操作。此时不需要像顺序表一样,移动元素。链表的插入 只需要修改指向即可。
3、顺序表还有不好的地方,就是你需要看满不满,满了要扩容,扩容了之后,不一定都能放完。所以,他空间上的利用率不高。
4.2数组和链表的区别
链表随用随取 要一个new一个
而数组则不一样 数组是一开始就确定好的
4.3AeeayList和LinkedList的区别
集合框架当中的两个类
集合框架就是将 所有的数据结构,封装成Java自己的类
以后我们要是用到顺序表了 直接使用ArrayList就可以。