Version: Next
双向链表
特征
- 双向链表的成员变量包括
size:节点数目first:指向头结点last:指向尾结点
 - 双向链表的节点成员属性包括:
element:节点值prev:指向前一个节点next:指向下一个节点
 

双向链表节点
private static class BiLinkedListNode<E> {
    E element;
    BiLinkedListNode<E> next;
    BiLinkedListNode<E> prev;
    public BiLinkedListNode() {
    }
    public BiLinkedListNode(E element, BiLinkedListNode<E> prev, BiLinkedListNode<E> next) {
        this.element = element;
        this.prev = prev;
        this.next = next;
    }
    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        if (prev == null) {
            stringBuilder.append("[null]");
        } else {
            stringBuilder.append("[").append(prev.element).append("]");
        }
        stringBuilder.append("[").append(element).append("]");
        if (next == null) {
            stringBuilder.append("[null]");
        } else {
            stringBuilder.append("[").append(next.element).append("]");
        }
        return stringBuilder.toString();
    }
}
双向链表成员变量
public class BiLinkedList<E> {
    private static final int ELEMENT_NOT_FOUND = -1;
    private int size = 0;
    private BiLinkedListNode<E> first;
    private BiLinkedListNode<E> last;
    //...
}
node(int index)方法
给定index,返回对应的节点对象
与单向链表的区别
- 在单向链表中,只能从一个方向遍历查找一个节点
 - 在双向链表中,可以通过判断,决定从头还是从尾开始遍历查找节点,这样可以减少遍历的总节点数
- index小于size的一半,从头节点开始遍历
 - index大于size的一半,从尾节点开始遍历
 
 
private BiLinkedListNode<E> node(int index) {
    rangeCheck(index);
    BiLinkedListNode<E> node;
    if (index < (size >> 1)) { // 正向遍历
        node = first;
        for (int i = 0; i < index; i++) {
            node = node.next;
        }
    } else { // 反向遍历
        node = last;
        for (int i = size - 1; i > index; i--) {
            node = node.prev;
        }
    }
    return node;
}
toString方法
@Override
public String toString() {
    StringBuilder result = new StringBuilder("BiLinkedList{" +
            "size=" + size + " | ");
    if (first == null) { // 如果链表为空,不遍历直接打印空
        result.append("null }");
        return result.toString();
    }
    BiLinkedListNode<E> node = first;
    while (node.next != null) {
        result.append(node).append(" <-> ");
        node = node.next;
    }
    result.append(node).append("}");
    return result.toString();
}
get(int index)方法
不需要修改
//  返回index位置对应的元素
public E get(int index) {
    return node(index).element;
}
set(int index, E element)方法
不需要修改
//  设置Index位置的元素
public E set(int index, E element) {
    BiLinkedListNode<E> node = node(index);
    E old = node.element;
    node.element = element;
    return old;
}
clear方法
需要将尾结点引用置空,链表才能被垃圾回收器回收
//  清除所有元素
public void clear() {
    size = 0;
    first = null;
    last = null;
}
add(int index, E element)方法
- 找到index对应节点
 - 获取index节点的前一个和后一个
 - 创建要插入的节点,设置前一个节点为index的前一个节点,后一个节点为index
 - 将前一个节点的next设置为要插入的节点
 - 将下一个节点的prev设置为要插入的节点
 
//  向index位置插入一个元素
public void add(int index, E element) {
    rangeCheckForAdd(index);
    // 找到index对应的节点
    BiLinkedListNode<E> node = node(index);
    BiLinkedListNode<E> preNode = node.prev;  // 前一个节点
    // 创建要插入的节点
    BiLinkedListNode<E> insertNode = new BiLinkedListNode<>(element, preNode, node);
    // 前一个节点的next指向要插入的节点
    preNode.next = insertNode;
    // 后一个节点的prev指向要插入的节点
    node.prev = insertNode;
    size++;
}
处理边界情况
- 在开头插入
- 则不存在前驱节点,必须进行判断,否则会产生空指针
 - 插进去的节点就是头节点,因此要将链表的
first指向插入的节点 
 - 在尾巴插入
- 相当于index的值等于size值的情况
 - 插入节点的next为null
 - 插入节点的prev为先前双向链表的last
 - 新插入的节点变成了新的last
 - 先前last的next指向新插入的节点
 
 - 在尾巴插入,且当前链表为空
- size为0,插入了一个节点,那么这个节点是唯一的节点,first、last都是它
 
 
//  向index位置插入一个元素
public void add(int index, E element) {
    rangeCheckForAdd(index);
    // 在最后面插入
    if (index == size) {
        // 在最后面插入新节点,新节点的next为空,prev为上一个last
        // 最终插进去的节点变为了新的last
        last = new BiLinkedListNode<>(element, last, null);
        if (size == 0) { // 链表为空
            first = last; // 唯一一个节点,头 尾都是它
        } else {
            // 先前last的next指向新插入节点,也就是新的last节点
            last.prev.next = last;
        }
    } else { // 不是在最后面插入
        // 找到index对应的节点
        BiLinkedListNode<E> node = node(index);
        BiLinkedListNode<E> preNode = node.prev;  // 前一个节点
        // 创建要插入的节点
        BiLinkedListNode<E> insertNode = new BiLinkedListNode<>(element, preNode, node);
        if (preNode == null) { // 在最开头插入
            first = insertNode;
        } else {
            // 前一个节点的next指向要插入的节点
            preNode.next = insertNode;
        }
        // 后一个节点的prev指向要插入的节点
        node.prev = insertNode;
    }
    size++;
}
remove(int index)方法
- 找到index对应的节点node
 - node.prev的next指向node.next
 - node.next的prev指向node.prev
 - size--
 
//  移除index位置的元素并返回
public E remove(int index) {
    BiLinkedListNode<E> node = node(index);
    E removedElement = node.element;
    node.prev.next = node.next;
    node.next.prev = node.prev;
    size--;
    return removedElement;
}
边界情况
- 删除的元素是第一个元素
- first指向node的next
 - node的next节点的prev指向null
 
 - 删除的元素是最后一个
- last指向node的prev
 - node的prev节点的next指向null
 
 - 链表只有一个元素
- first设置为null, last设置为null
 
 
//  移除index位置的元素并返回
public E remove(int index) {
    BiLinkedListNode<E> node = node(index);
    E removedElement = node.element;
    if (size == 1) { // 删除唯一节点
        first = null;
        last = null;
    } else if (node == first) { // 删除的是头结点
        first = node.next;
        node.next.prev = null;
    } else if (node == last) { // 删除的是尾结点
        last = node.prev;
        node.prev.next = null;
    } else {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }
    size--;
    return removedElement;
}