LinkedList

Collection 系列文章的总目录:

使用双向链表

  • 经典的双向链表实现:prev/next 指针

查找非常慢:O(n)

从头/尾部删除:O(1)

  • 因此非常适合作为队列

源码导读:

可以看到 LinkedList 实现了双端队列 Deque:

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

同时它是串行访问的 List:

// 提供串行访问的List的骨架(skeletal)
AbstractSequentialList extends AbstractList

// 对于随机访问的数组,建议使用AbstractList

属性:

// 存在的元素个数
transient int size = 0;

// 头节点指针
transient Node<E> first;

// 尾节点指针
transient Node<E> last;

节点:

 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;
     }
 }

添加元素:

add(E e):

  • 调用 linkLast(e)
  • 和 addLast(E e) 一样,只是多个 return true
  • 所以是往尾部 add
public boolean add(E e) {
    linkLast(e);
    return true;
}

获取元素:

get(int index):

  • 不推荐使用,因为它会遍历链表。O(n)
  • 内部做了优化:
    • if (index < (size >> 1)),则从头节点开始找
    • 反之,从尾节点开始找
    • 所以消耗的时间缩短了一半
    • 不过时间复杂度是不计算系数的,所以还是O(n)
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;
    }
}

删除元素:

remove():

  • 直接调用 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;
}

Deque 常见方法:

  • 右侧尾部入队,左侧头部出队
  • add(E):入队。对于容量有限制的实现,如果超过容量,会抛出 IllegalStateException 异常
  • addFirst/addLast
  • offer(E):入队。不会抛出异常
  • offerFirst/offerLast
  • remove():出队。如果队列为空,会抛出 NoSuchElementException 异常
  • removeFirst/removeLast
  • remove(Object):删除并返回第一个找到的元素。允许传入null
    • 所以 Deque 允许存入 null,LinkedList 也允许存入 null
  • poll():出队,如果队列为空,返回 null。不抛出异常
  • pollFirst/pollLast
  • peek():偷看。即查看队头元素,但不取出或删除元素。
  • peekFirst/peekLast
  • 以下两个是栈的方法,双端队列同时可作为栈使用
  • push():入栈
  • pop():出栈

面试考点:

1、核心数据结构:双向链表

2、时间复杂度:

  • 查找:O(n)
  • 查找 + 插入/删除:O(n)
  • 头尾插入/删除:O(1)

3、和 ArrayList 的区别和联系:

  • 核心数据结构不同,ArrayList 是数组,LinkedList 是双向链表
  • 使用场景不同

4、常见算法题:

  • 检测单链表成环:快慢双指针,当两个指针相遇,则说明有环
  • 反转单链表:递归和非递归
  • 分别将奇数的元素拆开,单独变成一条链表
posted @ 2020-03-30 00:46  demo杰  阅读(324)  评论(0编辑  收藏  举报