數據結構與算法——鏈表

1、概念

  鏈表是一種將一組不連續的內存塊串聯起來起來使用的線性表,其中內存塊稱爲鏈表的結點,記錄下個結點地址的“指針”稱爲後繼指針,雙鏈表中還有記錄上個結點地址的“指針”稱爲前驅指針

2、特點

2.1、空間結構

單鏈表 循環鏈表 雙向鏈表
結構 尾結點指針指向空地址null 尾結點指針向頭結點 後繼指針指向後面的結點
前驅指針指向前面的結點
空間 結點數據、下一個元素的地址(尾結點爲null) 結點數據、下一個元素的地址(尾結點爲第一個元素的地址) 上一個元素的地址、結點數據、下一個元素的地址

2.2、高效的插入和刪除,低效的隨機訪問

插入和刪除的時間複雜度:O(1)

訪問的時間複雜度:O(n)

3、鏈表和數組對比

3.1、鏈表優勢

插入刪除高效

無拷貝的動態擴容

3.2、數組優勢

隨機訪問高效

內存佔用小

4、其他

4.1、手撕LinkedList

package com.company;

import java.util.NoSuchElementException;

/**
 * 功能描述:
 *
 * @author liuchaoyong
 * @version 1.0
 * @date 2020/1/15 14:45
 */
public class MyLinkedList<E> {

    /**
     * 鏈表長度
     */
    transient int size = 0;

    /**
     * 哨兵結點(頭),指向鏈表頭結點
     */
    transient Node<E> first;

    /**
     * 哨兵結點(尾),指向鏈表尾結點
     */
    transient Node<E> last;

    public MyLinkedList() {

    }

    /**
     * 獲取鏈表頭結點(爲空拋異常)
     */
    public E getFirst() {
        final Node<E> f = first;
        if (f == null) {
            throw new NoSuchElementException();
        }
        return f.item;
    }

    /**
     * 獲取鏈表尾結點(爲空拋異常)
     */
    public E getLast() {
        final Node<E> l = last;
        if (l == null) {
            throw new NoSuchElementException();
        }
        return l.item;
    }

    /**
     * 移除鏈表頭結點
     */
    public E removeFirst() {
        final Node<E> f = first;
        if (f == null) {
            throw new NoSuchElementException();
        }
        return unlinkFirst(f);
    }

    /**
     * 移除鏈表尾結點
     */
    public E removeLast() {
        final Node<E> l = last;
        if (l == null) {
            throw new NoSuchElementException();
        }
        return unlinkLast(l);
    }

    /**
     * 將元素添加至鏈表頭結點
     */
    public void addFirst(E element) {
        linkedFirst(element);
    }

    /**
     * 將元素添加至鏈表尾結點
     */
    public void addLast(E element) {
        linkedLast(element);
    }

    /**
     * 鏈表中是否包含某元素
     */
    public boolean contains(Object o) {
        return indexOf(o) != -1;
    }

    /**
     * 獲取鏈表擁有元素個數
     */
    public int size() {
        return size;
    }

    /**
     * 將元素添加至鏈表尾結點,返回執行結果
     */
    public boolean add(E element) {
        addLast(element);
        return true;
    }

    /**
     * 從鏈表中移除指定元素
     */
    public boolean remove(Object o) {
        if (o == null) {
            for (Node<E> node = first; node != null; node = node.next) {
                if (node.item == null) {
                    unlink(node);
                    return true;
                }
            }
        } else {
            for (Node<E> node = first; node != null; node = node.next) {
                if (o.equals(node.item)) {
                    unlink(node);
                    return true;
                }
            }
        }
        return false;
    }


    /**
     * 獲取元素頭次出現的位置
     */
    public int indexOf(Object o) {
        int index = 0;
        if (o == null) {
            for (Node<E> node = first; node != null; node = node.next) {
                if (node.item == o) {
                    return index;
                }
                index++;
            }
        } else {
            for (Node<E> node = first; node != null; node = node.next) {
                if (o.equals(node.item)) {
                    return index;
                }
                index++;
            }
        }
        return -1;
    }

    /**
     * 獲取元素最後一次出現的位置
     */
    public int lastIndexOf(Object o) {
        int index = size;
        if (o == null) {
            for (Node<E> node = last; node != null; node = node.prev) {
                if (node.item == o) {
                    return index;
                }
                index--;
            }
        } else {
            for (Node<E> node = last; node != null; node = node.prev) {
                if (o.equals(node.item)) {
                    return index;
                }
                index--;
            }
        }
        return -1;
    }

    /**
     * 清空鏈表元素
     */
    public void clear() {
        for (Node<E> x = first; x != null; ) {
            final Node<E> next = x.next;
            x.prev = null;
            x.item = null;
            x.next = null;
            x = next;
        }
        first = last = null;
        size = 0;
    }

    /**
     * 獲取鏈表某位置元素
     */
    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }

    /**
     * 替換鏈表中指定位置的元素,並返回舊元素
     */
    public E set(int index, E element) {
        checkElementIndex(index);
        Node<E> x = node(index);
        E item = x.item;
        x.item = element;
        return item;
    }

    /**
     * 向鏈表指定位置添加元素(範圍爲:鏈表頭~鏈表尾)
     */
    public void add(int index, E element) {
        checkPositionIndex(index);
        Node<E> node = node(index);
        Node<E> prev = node.prev;
        Node<E> newNode = new Node<>(prev, element, node);
        if (prev == null) {
            first = newNode;
        } else {
            node.prev = newNode;
        }
        size++;
    }

    /**
     * 移除鏈表指定位置元素
     */
    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }

    /**
     * 獲取鏈表頭結點(爲空返空)
     */
    public E peek() {
        Node<E> first = this.first;
        return (first == null) ? null : first.item;
    }

    /**
     * 獲取鏈表頭結點(爲空拋異常),同getFirst()
     */
    public E element() {
        return getFirst();
    }

    /**
     * 移除鏈表頭結點(爲空返空)
     */
    public E poll() {
        Node<E> first = this.first;
        return (first == null) ? null : unlink(first);
    }

    /**
     * 移除鏈表頭結點(爲空拋異常),同removeFirst()
     */
    public E remove() {
        return removeFirst();
    }

    /**
     * 將元素添加至鏈表尾結點(同add)
     */
    public boolean offer(E e) {
        return add(e);
    }

    /**
     * 將元素添加至鏈表頭結點,返回執行結果
     */
    public boolean offerFirst(E e){
        addFirst(e);
        return true;
    }

    /**
     * 將元素添加至鏈表尾結點,返回執行結果
     */
    public boolean offerLast(E e){
        addLast(e);
        return true;
    }

    /**
     * 獲取鏈表頭結點,同peek()
     */
    public E peekFirst() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
    }

    /**
     * 獲取鏈表尾結點
     */
    public E peekLast(){
        final Node<E> f = last;
        return (f == null) ? null : f.item;
    }

    /**
     * 移除鏈表頭結點
     */
    public E pollFirst(){
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }

    /**
     * 移除鏈表尾結點
     */
    public E pollLast(){
        final Node<E> f = last;
        return (f == null) ? null : unlinkLast(f);
    }

    /**
     * 將元素添加至鏈表頭結點,同addFirst()
     */
    public void push(E e){
        addFirst(e);
    }

    /**
     * 移除鏈表頭結點,同removeFirst()
     */
    public void pop(){
        removeFirst();
    }

    /**
     * 移除鏈表中第一個包含此元素的結點,返回執行結果
     */
    public boolean removeFirstOccurence(Object o){
        return remove(o);
    }

    /**
     * 移除鏈表中最後一個包含此元素的結點,返回執行結果
     */
    public boolean removeLastOccurence(Object o){
        if (o == null) {
            for (Node<E> node = last; node != null; node = node.prev) {
                if (node.item == null) {
                    unlink(node);
                    return true;
                }
            }
        } else {
            for (Node<E> node = last; node != null; node = node.prev) {
                if (o.equals(node.item)) {
                    unlink(node);
                    return true;
                }
            }
        }
        return false;
    }

    private static class Node<E> {

        /**
         * 上一結點地址(可見LinkedList是一個雙向鏈表)
         */
        Node<E> prev;

        /**
         * 當前結點數據
         */
        E item;

        /**
         * 下一結點地址
         */
        Node<E> next;

        Node(Node<E> prev, E item, Node<E> next) {
            this.prev = prev;
            this.item = item;
            this.next = next;
        }
    }

    /**
     * 鏈接頭結點
     */
    private void linkedFirst(E element) {
        //備份哨兵結點(頭)
        final Node<E> f = first;
        //新建一個結點,並將哨兵結點(頭)指向該結點
        final Node<E> newNode = new Node<>(null, element, f);
        first = newNode;
        //如果原來的頭結點爲null,說明鏈表爲空,此時添加的結點,既是頭結點又是尾結點,所以將哨兵結點(尾)指向該結點
        //否則之前鏈表中已有數據,將原頭結點(也就是備份的哨兵結點(頭))的前驅指針指向新的頭結點
        if (f == null) {
            last = newNode;
        } else {
            f.prev = newNode;
        }
        size++;
    }

    /**
     * 鏈接尾結點
     */
    private void linkedLast(E element) {
        //備份哨兵結點(尾)
        final Node<E> l = last;
        //新建一個結點,並將哨兵結點(尾)指向該結點
        final Node<E> newNode = new Node<>(l, element, null);
        last = newNode;
        //如果原來的尾結點爲null,說明鏈表爲空,此時添加的結點,既是尾結點又是頭結點,所以將哨兵結點(頭)指向該結點
        //否則之前鏈表中已有數據,將原尾結點(也就是備份的哨兵結點(尾))的後繼指針指向新的尾結點
        if (l == null) {
            first = newNode;
        } else {
            l.next = newNode;
        }
        size++;

    }

    /**
     * 取消鏈接頭結點
     */
    private E unlinkFirst(Node<E> f) {
        //備份頭結點數據和下一結點的位置
        final E item = f.item;
        final Node<E> next = f.next;
        //讓GC回收
        f.item = null;
        f.next = null;
        //將哨兵結點(頭)指向下一結點
        first = next;
        //如果下一結點爲null,說明鏈表只有一個結點,此時要把哨兵結點(尾)也指向null
        //否則就將下一結點的前驅指針從原來的結點指向null,表示該結點是頭結點
        if (next == null) {
            last = null;
        } else {
            next.prev = null;
        }
        size--;
        return item;
    }

    /**
     * 取消鏈接尾結點
     */
    private E unlinkLast(Node<E> l) {
        //備份尾結點和上一結點的位置
        final E item = l.item;
        final Node<E> prev = l.prev;
        //讓GC回收
        l.item = null;
        l.prev = null;
        //將哨兵結點(尾)指向上一結點
        last = prev;
        //如果上一結點爲null,說明鏈表只有一個結點,此時要把哨兵結點(頭)也指向null
        //否則就將上一結點的後繼指針從原來的結點指向null,表示該結點是尾結點
        if (prev == null) {
            first = null;
        } else {
            prev.next = null;
        }
        size--;
        return item;
    }

    /**
     * 取消鏈接前驅結點和後繼結點
     */
    E unlink(Node<E> x) {

        //final保證變量不變
        final E item = x.item;
        final Node<E> prev = x.prev;
        final Node<E> next = x.next;

        //如果該結點的前驅結點爲空,說明該結點是鏈表頭結點,此時將哨兵頭結點指向該結點
        //否則就將上一個結點的後繼結點指向該結點的後繼結點,並釋放該結點的前驅結點
        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }

        //如果該結點的後繼結點爲空,說明該結點是鏈表尾結點,此時將哨兵尾結點指向該結點
        //否則就將下一個即誒點的前驅結點指向該結點的前驅結點,並釋放該結點的後繼結點
        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }
        size--;
        return item;
    }

    /**
     * 下標越界檢查(for set)
     */
    private void checkElementIndex(int index) {
        if (!(index >= 0 && index < size)) {
            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
        }
    }

    /**
     * 下標越界檢查(for add)
     */
    private void checkPositionIndex(int index) {
        if (!(index >= 0 && index <= size)) {
            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
        }
    }

    /**
     * 獲取指定下標結點
     */
    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;
        }
    }

}
發佈了61 篇原創文章 · 獲贊 13 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章