在上一篇文章中,我们详细介绍了线性表数据结构的原理以及顺序存储结构,并结合ArrayList源码进行了分析,相关文章大家可以点击这里回看我的博客:线性表数据结构解读(一)顺序存储结构ArrayList
本篇文章,我将给大家继续解读线性表数据结构,这次我们来谈链式存储结构。
链式存储结构
链式存储结构是用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的,也可以是不连续的。
我们将数据元素和下一个元素位置的结构称为链表的节点。若第一个节点只表示整个链表的起始位置,而无任何信息,称其为头结点。对于最后一个结点,后面无任何元素,其表示元素位置的地址用“^”来表示,称其为尾结点,程序实现中用”null“表示。
链表中结点的表示必须要用到两个域,其中一个存放数组元素自身的信息ai,称其为数据域,另一个存放下一个元素的地址或位置,以保证链表的连续性,称其为指针。
链式存储结构的优缺点
优:删除和插入效率高
缺:查询效率低
链表的分类
● 单链表
是由第一个元素到最后一个元素构成的一个链,其特点是从第一个元素(可能有头指针和头结点)到最后一个元素(结束标志位^)够成的一个链,成为单链表。我们通过第一个元素的指针可以顺序找到后面元素所在的位置,因此所有操作全部是从第一个元素(头指针或头结点)开始的。
● 循环链表
在单链表中,最后一个元素的存储区域是^,如果将它指向第一个元素(头结点)位置,就构成了循环链表。循环链表的特点是在所有元素之间构成的一个环,从任何一个元素出发,都可以查找其他所有元素,同时还充分利用了空间。
● 双向循环链表
双向循环链表是单向循环链表的每个结点中,再设置一个指向其前驱结点的指针域。也就是说,可以从任何一个元素出发,向两个方向分别查找相应的元素,可以提高操作效率。
● 空的双向循环链表
在Java中,我们常见具有代表性的链式存储结构有很多,这里我们以LinkedList为例,进行分析,看看它内部是如何实现链式存储结构的,由于源码过长,这里我们重点分析增删改查和迭代器方法。
构造方法
public class LinkedList<E> extends AbstractSequentialList<E> implements
List<E>, Deque<E>, Queue<E>, Cloneable, Serializable {
private static final long serialVersionUID = 876323262645176354L;
transient int size = 0;
transient Link<E> voidLink;// 头指针
private static final class Link<ET> {
// 内部精简后的静态Link类,这个其实就是一个结点
ET data;
Link<ET> previous, next;// 双向链表
Link(ET o, Link<ET> p, Link<ET> n) {
data = o;
previous = p;
next = n;
}
}
/**
* LinkedList无参构造
*/
public LinkedList() {
// 实例化头指针
voidLink = new Link<E>(null, null, null);
// 分别让头指针的previous和next等于头指针
voidLink.previous = voidLink;
voidLink.next = voidLink;
}
/**
* 接收一个Collection参数的LinkedList构造方法
*/
public LinkedList(Collection<? extends E> collection) {
this();
addAll(collection);
}
迭代器
// 作为一个List,LinkedList肯定也包含一个迭代器
private static final class LinkIterator<ET> implements ListIterator<ET> {
int pos, expectedModCount;
final LinkedList<ET> list;
// link表示当前正在遍历的指针,lastLink表示最后的节点
Link<ET> link, lastLink;
LinkIterator(LinkedList<ET> object, int location) {
list = object;
expectedModCount = list.modCount;
if (location >= 0 && location