线性表数据结构解读(二)链式存储结构 LinkedList

745 阅读5分钟

    在上一篇文章中,我们详细介绍了线性表数据结构的原理以及顺序存储结构,并结合ArrayList源码进行了分析,相关文章大家可以点击这里回看我的博客:线性表数据结构解读(一)顺序存储结构ArrayList
    本篇文章,我将给大家继续解读线性表数据结构,这次我们来谈链式存储结构。

链式存储结构

链式存储结构是用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的,也可以是不连续的。

    我们将数据元素和下一个元素位置的结构称为链表的节点。若第一个节点只表示整个链表的起始位置,而无任何信息,称其为头结点。对于最后一个结点,后面无任何元素,其表示元素位置的地址用“^”来表示,称其为尾结点,程序实现中用”null“表示。
    链表中结点的表示必须要用到两个域,其中一个存放数组元素自身的信息ai,称其为数据域,另一个存放下一个元素的地址或位置,以保证链表的连续性,称其为指针。

链式存储结构的优缺点

优:删除和插入效率高
缺:查询效率低

链表的分类

● 单链表

是由第一个元素到最后一个元素构成的一个链,其特点是从第一个元素(可能有头指针和头结点)到最后一个元素(结束标志位^)够成的一个链,成为单链表。我们通过第一个元素的指针可以顺序找到后面元素所在的位置,因此所有操作全部是从第一个元素(头指针或头结点)开始的。

这里写图片描述

● 循环链表

在单链表中,最后一个元素的存储区域是^,如果将它指向第一个元素(头结点)位置,就构成了循环链表。循环链表的特点是在所有元素之间构成的一个环,从任何一个元素出发,都可以查找其他所有元素,同时还充分利用了空间。

这里写图片描述

● 双向循环链表

双向循环链表是单向循环链表的每个结点中,再设置一个指向其前驱结点的指针域。也就是说,可以从任何一个元素出发,向两个方向分别查找相应的元素,可以提高操作效率。

这里写图片描述

● 空的双向循环链表

这里写图片描述

在Java中,我们常见具有代表性的链式存储结构有很多,这里我们以LinkedList为例,进行分析,看看它内部是如何实现链式存储结构的,由于源码过长,这里我们重点分析增删改查和迭代器方法。

构造方法

public class LinkedList extends AbstractSequentialList implements
        List, Deque, Queue, Cloneable, Serializable {

    private static final long serialVersionUID = 876323262645176354L;

    transient int size = 0;

    transient Link voidLink;

    private static final class Link {
        ET data;

        Link previous, next;

        Link(ET o, Link p, Link n) {
            data = o;
            previous = p;
            next = n;
        }
    }
    /**
     * LinkedList无参构造
     */
    public LinkedList() {
        voidLink = new Link(null, null, null);
        voidLink.previous = voidLink;
        voidLink.next = voidLink;
    }

    /**
     * 接收一个Collection参数的LinkedList构造方法
     */
    public LinkedList(Collection collection) {
        this();
        addAll(collection);
    }

迭代器

    
    private static final class LinkIterator implements ListIterator {
        int pos, expectedModCount;

        final LinkedList list;
        
        Link link, lastLink;

        LinkIterator(LinkedList object, int location) {
            list = object;
            expectedModCount = list.modCount;
            if (location >= 0 && location  list.size) {
                
                
                
                link = list.voidLink;
                if (location < list.size / 2) {
                    for (pos = -1; pos + 1 < location; pos++) {
                        link = link.next;
                    }
                } else {
                    for (pos = list.size; pos >= location; pos--) {
                        link = link.previous;
                    }
                }
            } else {
                throw new IndexOutOfBoundsException();
            }
        }
        
        public void add(ET object) {
            if (expectedModCount == list.modCount) {
                Link next = link.next;
                Link newLink = new Link(object, link, next);
                link.next = newLink;
                next.previous = newLink;
                link = newLink;
                lastLink = null;
                pos++;
                expectedModCount++;
                list.size++;
                list.modCount++;
            } else {
                throw new ConcurrentModificationException();
            }
        }

        public boolean hasNext() {
            return link.next != list.voidLink;
        }

        public boolean hasPrevious() {
            return link != list.voidLink;
        }

        public ET next() {
            if (expectedModCount == list.modCount) {
                LinkedList.Link next = link.next;
                if (next != list.voidLink) {
                    lastLink = link = next;
                    pos++;
                    return link.data;
                }
                throw new NoSuchElementException();
            }
            throw new ConcurrentModificationException();
        }

        public int nextIndex() {
            return pos + 1;
        }

        public ET previous() {
            if (expectedModCount == list.modCount) {
                if (link != list.voidLink) {
                    lastLink = link;
                    link = link.previous;
                    pos--;
                    return lastLink.data;
                }
                throw new NoSuchElementException();
            }
            throw new ConcurrentModificationException();
        }

        public int previousIndex() {
            return pos;
        }
        
        public void remove() {
            if (expectedModCount == list.modCount) {
                if (lastLink != null) {
                    Link next = lastLink.next;
                    Link previous = lastLink.previous;
                    next.previous = previous;
                    previous.next = next;
                    if (lastLink == link) {
                        pos--;
                    }
                    link = previous;
                    lastLink = null;
                    expectedModCount++;
                    list.size--;
                    list.modCount++;
                } else {
                    throw new IllegalStateException();
                }
            } else {
                throw new ConcurrentModificationException();
            }
        }
        
        public void set(ET object) {
            if (expectedModCount == list.modCount) {
                if (lastLink != null) {
                    lastLink.data = object;
                } else {
                    throw new IllegalStateException();
                }
            } else {
                throw new ConcurrentModificationException();
            }
        }
    }

添加方法

    /**
     * 添加方法,在指定位置进行添加
     * @param location the index at which to insert.
     * @param object the object to add.
     * @throws IndexOutOfBoundsException
     *             if {@code location < 0 || location > size()}
     */
    @Override
    public void add(int location, E object) {
        if (location >= 0 && location <= size) {
            Link link = voidLink;
            
            if (location < (size / 2)) {
                for (int i = 0; i <= location; i++) {
                    link = link.next;
                }
            } else {
                for (int i = size; i > location; i--) {
                    link = link.previous;
                }
            }
            
            Link previous = link.previous;
            Link newLink = new Link(object, previous, link);
            previous.next = newLink;
            link.previous = newLink;
            size++;
            modCount++;
        } else {
            throw new IndexOutOfBoundsException();
        }
    }

    /**
     * 将元素(E)添加到LinkedList中
     * @param object the object to add.
     * @return always true
     */
    @Override
    public boolean add(E object) {
        return addLastImpl(object);
    }

为了方便大家理解,下面我将画图来解释下插入结点的实现机制

这里写图片描述

删除方法

    /**
     * Removes the object at the specified location from this {@code LinkedList}.
     * @param location the index of the object to remove
     * @return the removed object
     * @throws IndexOutOfBoundsException
     *             if {@code location < 0 || location >= size()}
     */
    @Override
    public E remove(int location) {
        
        if (location >= 0 && location < size) {
            Link link = voidLink;
            
            if (location < (size / 2)) {
                for (int i = 0; i <= location; i++) {
                    link = link.next;
                }
            } else {
                for (int i = size; i > location; i--) {
                    link = link.previous;
                }
            }
            Link previous = link.previous;
            Link next = link.next;
            previous.next = next;
            next.previous = previous;
            size--;
            modCount++;
            
            return link.data;
        }
        throw new IndexOutOfBoundsException();
    }

获取方法

    @Override
    public E get(int location) {
        
        if (location >= 0 && location < size) {
            Link link = voidLink;
            
            if (location < (size / 2)) {
                for (int i = 0; i <= location; i++) {
                    link = link.next;
                }
            } else {
                for (int i = size; i > location; i--) {
                    link = link.previous;
                }
            }
            return link.data;
        }
        throw new IndexOutOfBoundsException();
    }

修改方法

    /**
     * Replaces the element at the specified location in this {@code LinkedList}
     * with the specified object.
     *
     * @param location
     *            the index at which to put the specified object.
     * @param object
     *            the object to add.
     * @return the previous element at the index.
     * @throws ClassCastException
     *             if the class of an object is inappropriate for this list.
     * @throws IllegalArgumentException
     *             if an object cannot be added to this list.
     * @throws IndexOutOfBoundsException
     *             if {@code location < 0 || location >= size()}
     */
    @Override
    public E set(int location, E object) {
        
        if (location >= 0 && location < size) {
            Link link = voidLink;
            
            if (location < (size / 2)) {
                for (int i = 0; i <= location; i++) {
                    link = link.next;
                }
            } else {
                for (int i = size; i > location; i--) {
                    link = link.previous;
                }
            }
            
            E result = link.data;
            link.data = object;
            return result;
        }
        throw new IndexOutOfBoundsException();
    }

上一篇文章:线性表数据结构解读(一)顺序存储结构ArrayList