链表是什么
链表是一种很常见的数据结构,是一种线性结构。他的数据形式类似我们日常认知中的锁链,使用每个节点保存下一个节点的指针的方式实现关联。
链表在删除和新增的时候,只需要修改前一个结点的指向位置即可,和数组相比并不需要进行整体位移,所以链表在修改操作上比数组要优秀。
简单的链表示例
使用链表的集合
我们长接触的链表结构主要有LinkedList、LinkedHashMap,队列、栈等。
链表分类
我们目前经常用到的链表主要有下面几种:
- 单链表
- 双向链表
- 循环链表
单向链表
单向链表的一个节点被分为了两个部分,一部分为节点数据保存了节点了信息。一部分为next的地址。
根据这个结构,单向链表只能向一个方向进行遍历,查找一个节点的时候需要从第一个节点开始每次访问下一个节点,直到找到需要的位置。
图示
操作
新增
此时为原始数据
在B和C之间添加O节点
将O的next坐标设置为之前B的next
流程为:
- 定位需要添加节点的位置B。
- 将B指向的nextc,修改为o
- 将O中next指向为c。此时完成数据插入
删除
此时为原始数据,准备一处O节点
此时获得O节点的上一节点,同时获取O节点next的指向信息
流程为:
- 遍历链表找到需要删除的节点的节点以及其上一节点。
- 将上一节点的next指向修改为被删除节点的next
双向链表
链表中的元素,除了能够指向下一个节点,同时还能够指向上一个元素节点,每个节点都存在两个指针。整个关联关系从前向后和从后向前都是可以的。
图示
操作
新增
添加节点O,修改B和C节点的坐标
流程为:
- 确定需要插入节点的位置。
- 将插入位置的上一节点的next指向新的节点,然后将原来下一节点的pre指向新的节点
- 添加新的节点的next和pre分别指向新的前后坐标
删除
流程为:
- 首先确定删除节点,然后根据pre和next确定其前后节点
- 将pre节点的next指向为被删除节点的next,将next节点的pre指向为被删除节点的pre信息
循环链表
循环链表指的是在链表的最后一个结点上指向第一个节点从而实现循环
图示
判断循环链表
一个完美的圆形链表可以从某处开始循环当再次遍历到此位置的时候可以确定其为循环链表。但是对于其他情况则不能这样使用。比如这种部分循环的链表。在执行遍历后链表会在某处进入生死循环。
所以我们可以使用快慢指针的思想的思想去判断是否循环链表。
快慢指针的原理是:我们定义两个节点指针fast、slow。对这两个指针设置不同的步长,比如fast步长2、slow步长1。然后让他们同时从头部节点开始遍历链表,如果链表是循环的则快慢指针总会相遇。
当然除了快慢指针我们还可以将我们之前遍历过的数据缓存起来。每次遍历下一节点的时候去缓存中比对下此节点是否已经被遍历过。如果缓存中已经存在则证明链表中存在环形结构。
栈
栈也是一种线性表,但是和链表不同之处。栈只允许从尾部操作数据。也就是只能对尾部的元素进行新增和删除操作。它按照先进先出原则进行操作(last in first out)
栈的数据添加和移除
java中的栈
java中java.util.Stack
实现了出栈和入栈的操作,其通过继承Vector添加了自己的方法来实现入栈和出栈,还是用的父类Vector的方法来执行元素操作。
栈的操作
初始化
Stack s = new Stack();
压栈
压栈的时候使用
s.push("");
出栈
按照真正的栈的操作,执行出栈的时候栈顶元素会被移除,但是在java.util.Stack
中提供了两个方法
peek:会查看栈顶元素但是并不会移除它。
pop:类似真正的出栈操作,会移除栈顶元素
s.peek();
s.pop();
判空
java为栈提供了empty
方法来进行空值判断。
遍历
java为栈提供了removeAllElements
方法来进行空值判断。
清空
java为栈提供了elements
方法来进行空值判断。
队列
和栈相反,队列是一种只允许一端进行插入而在另一端进行删除的线性结构
队列的操作
假如存在以下队列
删除
执行删除操作的时候会将头部元素取出
插入
当我们插入数据的时候只能从尾部插入
java中的队列
JAVA中使用了java.util.Queue
来实现了队列的相关内容。
双端队列
在JAVA中对队列做了一个扩展使用了java.util.Deque
定义了一种可以从头部和尾部进行删除操作的队列。
java中队列关系图
java中队列的实现
在JAVA中对于队列提供了两种实现:
java.util.concurrent.LinkedBlockingQueue
基于链表的实现
java.util.concurrent.ArrayBlockingQueue
基于数组的实现