zoukankan      html  css  js  c++  java
  • LinkedList的实现源码分析

    LinkedList
    以双向链表实现。链表无容量限制,但双向链表本身使用了更多空间,也需要额外的链表指针操作。
    按下标访问元素--get(i)/set(i,e) 要悲剧的遍历链表将指针移动到位(如果i>数组大小的一半,会从末尾移起)。
    插入、删除元素时修改前后节点的指针即可,但还是要遍历部分链表的指针才能移动到下标所指的位置,只有在链表两头的操作--add(), addFirst(),removeLast()或用iterator()上的remove()能省掉指针的移动。

    在介绍LinkedList之前我们来回顾下链表的类型,链表主要包含单向链表,单向循环链表,双向链表,双向循环链表。具体的图我就不在这边画出了,不清楚的可以自行百度。LinkedList是属于双向链表,下图是包含头结点和尾节点的双向链表。 ![](http://images2015.cnblogs.com/blog/184011/201604/184011-20160412152626332-986071185.jpg)

    本篇内容主要讲解下LinkedList这个双向链表在java的源码的实现,主要包含有add,remove,get,set等方法的介绍。

    首先我们来看下add方法的实现,下图演示了下节点插入的过程,包含了4个步骤。

    我们来看下java的源码,从代码可知insert节点是查在链表尾部的 ```java public boolean add(E e) { linkLast(e); return true; }

    void linkLast(E e) {
    final Node l = last;
    final Node newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
    first = newNode;
    else
    l.next = newNode;
    size++;
    modCount++;
    }

    private static class Node {
    E item;
    Node next;
    Node prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
    
    <p>因为add(E e)是插在末尾,所以实际上我们只需要做两部操作,吧NewNode的prew指向最后一个节点,把最后一个几点的next指向newNode
    ```java
        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++;
        }
    

    上面的insertBefore就是执行4个操作了:
    1.把新节点newNode的prev指向succ.prev
    2.把succ.prev的next执行newNode
    3.newNode的next指向succ
    4.succ的prev指向newNode

    第3、4步已经在Node的构造函数做了,java是执行顺序是3、4、1、2

    下面来看下删除的实现 ![](http://images2015.cnblogs.com/blog/184011/201604/184011-20160412152657926-1661468375.jpg)

    下面看下java的实现,具体下面代码逻辑很简单,就是获取要删除的节点x的prev节点为prev,x的下节点为next。
    然后按如图执行prev.next=next;next.prev=prev;

     E unlink(Node<E> x) {
            // assert x != null;
            final E element = x.item;
            final Node<E> next = x.next;
            final Node<E> prev = x.prev;
    
            if (prev == null) {
                first = next;
            } else {
                prev.next = next;
                x.prev = null;
            }
    
            if (next == null) {
                last = prev;
            } else {
                next.prev = prev;
                x.next = null;
            }
    
            x.item = null;
            size--;
            modCount++;
            return element;
        }
    

    set和get函数 ```java public E set(int index, E element) { checkElementIndex(index); Node x = node(index); E oldVal = x.item; x.item = element; return oldVal; }

    public E get(int index) {
    checkElementIndex(index);
    return node(index).item;
    }

    这个两个函数都用到了node函数,又都要用到查询,所以首先要判断index是否大于index/2,大于的话就从尾节点开始查,反正从前节点查。
    ```java
    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;
        }
    }
    

    这样的设计可以提高查询效率,时间复杂度为O(n/2)。

    参考

    数据结构与算法分析_java语言描述(第2版).韦斯

  • 相关阅读:
    跨域请求携带cookie
    vue keep-alive
    关于js replace 第二个参数时函数时,函数参数解析
    前端开发规范之CSS
    git命令集合(正在完善中...)
    怎么写jQuery的插件
    git命令集合
    GitHub创建静态网站预览方法
    正则表达式
    各种浏览器全屏模式的方法、属性和事件介绍
  • 原文地址:https://www.cnblogs.com/huaizuo/p/5382884.html
Copyright © 2011-2022 走看看