链表简介
链表是很常见的数据结构,由一个个节点组成,每个节点中储存着数据和指针(地址引用),指针负责节点间的连接。
它是一种线性表,线性表有两种存储方式:顺序存储和链式存储。链表属于链式存储,顺序由元素间的指针决定,元素在内存中非连续存放,且链表长度可以改变。数组是顺序存储的线性表,元素在内存中连续存放的,且数组创建时大小已固定。
链表可以用来实现栈和队列数据结构(栈和队列可理解为逻辑类数据结构,链表属于存储类数据结构),实现缓存LRU算法,Java类库也使用了链表(如,LinkedList,LinkedHashMap)等。链表的形式有很多,常用的有单向链表、双向链表、循环链表 ...
单向链表
单链表中的节点分两部分,分别是数据(data)和指向下一个节点的地址(next),尾节点(tail)的next指向null。单向链表只能从头到尾一个方向遍历,查找节点时需要从头节点(head)开始向下查找。
插入节点首先遍历查找到插入的位置,然后将当前插入节点的next指向下一节点,上一节点的next指向当前插入节点。删除节点同样从头遍历找到要删除的节点,然后将当前删除节点的上一个节点next指向当前删除节点的下一个节点。
节点的伪代码:
class Node<E>{
private E item;
private Node<E> next; // 如果是尾节点,next指向null
Node(E data, Node<E> next) {
this.item = data;
this.next = next;
}
// ...
}
单向循环链表
循环链表和非循环链表基本一样,区别是首尾节点连在了一起,最后一个节点的next指向头节点,形成了一个闭环。
节点的伪代码:
class Node<E>{
private E item;
// 如果是末尾节点,指向首节点的引用地址
private Node<E> next;
Node(E data, Node<E> next) {
this.item = data;
this.next = next;
}
// ...
}
双向链表
顾名思义,与单向链表相比较,双向链表可以从头到尾或从尾到头两个方向来遍历数据。双向链表中的节点分三个部分,分别是指向上一个节点的地址(prev)和数据(data)以及指向下一个节点的地址(next),尾节点(tail)节点的next指向null,头节点(head)的prev指向null。
增加和删除节点和单向链表同理,只是增加了修改prev地址的操作。
节点的伪代码:
class Node<E>{
private E item;
private Node<E> prev; // 头节点prev指向null
private Node<E> next; // 尾节点next指向null
Node(Node<E> prev, E data, Node<E> next) {
this.item = data;
this.prev = prev;
this.next = next;
}
// ...
}
双向循环链表
尾节点的next指向头节点,头结点的prev指向尾节点,首尾节点连在一起形成闭环。
节点的伪代码: