链表结构
不同于数组与向量中各数据项的物理存放位置与逻辑次序完全对应,链表中的元素虽然也构成一个线性逻辑次序,但其元素的物理地址可以任意。
链表中的元素维护一个前驱与后继,分别指向上一个元素与下一个元素的地址。
头尾节点与首末节点
链表结构中,除了可见的首末节点first、last,还有不可见的头尾节点header、trailer。
创建链表时即创建头尾节点,first时头节点的后继,last是尾节点的前驱。这种设计方法使得相关算法不必再对各种边界退化情况做专门处理。
Java中对链表的实现LinkedList
LinkedList中实现了一个内部私有类,其中next指向下一个节点,prev指向前一个节点,item是当前节点的值
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
另外还有两个内部私有变量size与modCount。
其中modCount用于记录LinkedList被修改的次数。
LinkedList提供两个构造方法
public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
LinkedList添加元素
在首次创建LinkedList时调用add(E e)函数,last == first == null。
将新创建的节点前驱指向LinkedList的last,即指向头节点null。
新节点newNode(l,e,null),将l即最后一个节点作为前驱,此时指向null。null作为后继,即指向不可见的尾节点,值为e。
将LinkedLits的last 指向新创建的节点。
满足 l == null,将LinkedList的first指向该节点。
此时列表中只有一个节点和隐含的两个不可见的头尾节点。
在LinkedList已有元素时调用add(E e),last指向最后一个节点,first指向第一个节点。
l = last 指向最后一个节点。
新节点newNode(l,e,null),将l即最后一个节点作为前驱。null作为后继,即指向不可见的尾节点,值为e。
将LinkedList的last替换为新节点newNode。
l != null,不满足条件语句将l的后继替换成新节点newNode,不再指向null。
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
在指定的索引处添加元素
当index == size,将e作为最后一个节点链接。
当index != size,将新节点(值为e)插入index的位置。node(index)的前驱作为新节点的前驱,node(index)作为新节点的后继。
public void add(int index, E element) {
checkPositionIndex(index);//对index进行有效性判定
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
/**
* Links e as last element.
*/
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
/**
* Inserts element e before non-null Node succ.
*/
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
/**
* Returns the (non-null) Node at the specified element index.
*/
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
删除节点,默认删除首节点。
public E remove() {
return removeFirst();
}
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
清除整个节点
public void clear() {
// Clearing all of the links between nodes is "unnecessary", but:
// - helps a generational GC if the discarded nodes inhabit
// more than one generation
// - is sure to free memory even if there is a reachable Iterator
for (Node<E> x = first; x != null; ) {
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
modCount++;
}