双链表
双链表在单链表的基础上进行扩展,一个节点中包含三部分:数据、前一个节点的指针、后一个节点的指针。双链表可以提高单链表的综合性能。在一个双链表中要维护着 first 指针和 last 指针 以及 链表的大小。
节点设计
private static class Node<E> {
E element;
Node next;
Node prev;
public Node(E elements, Node prev, Node next) {
this.element = elements;
this.next = next;
this.prev = prev;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (prev != null) {
builder.append(prev.element);
} else {
builder.append("null");
}
builder.append("_").append(element).append("_");
if (next != null) {
builder.append(next.element);
} else {
builder.append("null");
}
return builder.toString();
}
}
添加节点
在双向链表中添加元素的关键步骤就是连接两条线:前一个节点的next 、后一个节点的prev,有了这两个线就可以把两个节点连接到一起。默认是在尾部添加新节点。由于双向链表维护了尾部节点,所以在添加元素的时候就可以直接使用尾部节点,而不用向单链表一样每次都要查找出来尾部节点。
指定位置添加新节点
添加到首部位置
public void add(int index, E element) {
rangeCheckOfAdd(index);
// 顺序添加元素,直接在尾部进行添加
if (index == size) {
Node<E> oldLast = last;
// 维护最新的尾部节点
last = new Node<>(element, oldLast, null);
/**
* 如果是第一次 添加元素
* 头节点和尾节点都指向这个元素
*/
if (oldLast == null) {
first = last;
} else {
// 不是第一次添加
oldLast.next = last;
}
// 载指定的位置添加元素
} else {
// 找这个位置的节点
Node<E> next = node(index);
// 得到前一个节点
Node prev = next.prev;
// 新建节点,并且连接节点的前驱、后继节点
Node<E> node = new Node<>(element, prev, next);
// 为后继节点连接其前驱节点
next.prev = node;
/**
* 此时向第一个位置加入, 更新 first 指针
*/
if (prev == null) {
first = node;
} else {
// 连接前驱节点的后继节点
prev.next = node;
}
}
size++;
}
删除节点
对于链表来说,添加、删除操作简而言之就是对指针的改变 。得到要删除节点的前驱和后继节点,然后改变前驱节点的next指向、后继节点的prev指向即可。
普通删除
删除首部
删除尾部
public E remove(int index) {
rangeCheck(index);
// 得到这个节点
Node<E> node = node(index);
// 后继
Node next = node.next;
// 前驱
Node prev = node.prev;
/**
* 对删除首部、尾部元素做出特殊处理
*/
if (prev == null) {
// 删除首部元素
first = next;
} else {
prev.next = next;
}
if (next == null) {
// 删除尾部元素
last = prev;
} else {
next.prev = prev;
}
size--;
return node.element;
}