【数据结构】链表

一、何为链表

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

三、手写LinkedList

参考JDK中的LinkedList,写法基本一致,仅用于自己学习。

java 复制代码
public class LinkedList<E> implements List<E> {

    transient int size = 0;

    transient Node<E> first;

    transient Node<E> last;

    @Override
    public boolean add(E e) {
        linkLast(e); // 默认尾插
        return false;
    }

    /**
     * 头插法
     * @param e
     */
    private void linkFirst(E e) {
        Node<E> f = first;
        Node<E> newNode = new Node<>(e, null, f);
        first = newNode;
        if (f == null) {
            last = newNode;
        } else {
            f.prev = newNode;
        }
        size++;
    }

    /**
     * 尾插法
     * @param e
     */
    private void linkLast(E e) {
        Node<E> l = last;
        Node<E> newNode = new Node<>(e, l, null);
        last = newNode;
        if (l == null) {
            first = newNode;
        } else {
            l.next = newNode;
        }
        size++;
    }

    /**
     * 插链操作
     * @param e
     */
    private void unLink(Node<E> e) {
        Node<E> prev = e.prev;
        Node<E> next = e.next;
        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            e.prev = null;
        }
        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            e.next = null;
        }
        e.item = null;
        size--;
    }

    @Override
    public boolean addFirst(E e) {
        linkFirst(e);
        return true;
    }

    @Override
    public boolean addLast(E e) {
        linkLast(e);
        return true;
    }

    @Override
    public void remove(int index) {
        checkElementIndex(index);
        unLink(node(index));
    }

    private E unLinkFirst(Node<E> f) {
        E item = f.item;
        Node<E> next = f.next;
        f.item = null;
        f.next = null;
        first = next;
        if (next == null) {
            last = null;
        } else {
            next.prev = null;
        }
        size--;
        return item;
    }

    private E unLinkLast(Node<E> l) {
        E item = l.item;
        Node<E> prev = l.prev;
        l.item = null;
        l.prev = null;
        last = prev;
        if (prev == null) {
            first = null;
        } else {
            prev.next = null;
        }
        size--;
        return item;
    }

    @Override
    public E remove() { // 默认头删
        return removeFirst();
    }

    @Override
    public E removeFirst() {
        Node<E> f = first;
        if (f == null) {
            return null;
        }
        return unLinkFirst(f);
    }

    private Node<E> node(int 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;
        }
    }

    private void checkElementIndex(int index) {
        if (!isElementIndex(index)) {
            throw new RuntimeException(outOfBoundsMsg(index));
        }
    }

    private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }

    private String outOfBoundsMsg(int index) {
        return "Index: "+index+", Size: "+size;
    }

    @Override
    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }
    
    private static class Node<E> {
        E item;
        Node<E> prev;
        Node<E> next;
        public Node(E item, Node<E> prev, Node<E> next) {
            this.item = item;
            this.prev = prev;
            this.next = next;
        }
    }
}

四、常见问题

  • JDK中LinkedList是双向链表,add方法默认是尾插法,remove方法默认是头删,是实现LRU算法的雏形
  • 相比于ArrayList,LinkedList更适用于在首尾部进行插入删除和查询操作。ArrayList的耗时操作主要体现在对元素的拷贝和位移,而LinkedList的耗时操作主要体现在遍历定位和创建节点,因此在不同数据规模下耗时不同,不能简单的认为LinkedList一定比ArrayList插入快。
相关推荐
ulias2121 小时前
leetcode热题 - 5
数据结构·算法·leetcode
如何原谅奋力过但无声1 小时前
【灵神高频面试题合集04-05】二分查找
数据结构·python·算法·leetcode
我不是懒洋洋1 小时前
【数据结构】排序算法(直接插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序、归并排序、计数排序)
c语言·数据结构·c++·经验分享·算法·排序算法
辛苦才能1 小时前
数据结构--排序--插入排序(C语言,重点排序面试和比赛都会考察)
c语言·数据结构·面试
qeen8717 小时前
【数据结构】建堆的时间复杂度讨论与TOP-K问题
c语言·数据结构·c++·学习·
图码17 小时前
如何用多种方法判断字符串是否为回文?
开发语言·数据结构·c++·算法·阿里云·线性回归·数字雕刻
我星期八休息17 小时前
IT疑难杂症诊疗室:AI时代工程师Superpowers进化论
linux·开发语言·数据结构·人工智能·python·散列表
漂流瓶jz17 小时前
UVA-1152 和为0的4个值 题解答案代码 算法竞赛入门经典第二版
数据结构·算法·二分查找·题解·aoapc·算法竞赛入门经典·uva
你撅嘴真丑18 小时前
map 与 set容器的应用--话题焦点人物
数据结构
生成论实验室18 小时前
《事件关系阴阳博弈动力学:识势应势之道》第二篇:阴阳博弈——认知的动力学基础
数据结构·人工智能·科技·神经网络·算法