數據結構: Java中LinkedList的簡單實現

更新:以下代碼是初學鏈表後的簡單實現,時隔一年再看一下代碼。覺得有點慘不忍睹,測試了一下,標準庫的鏈表插入1000萬個元素用了10秒鐘,以下代碼花費的時間遠超標準庫。因爲如果要將元素插入到鏈表末尾,都需要遍歷整個鏈表。是個O(N)的算法。但實際上,大部分插入都是插入到最後(或是最前)。我們其實只需要維護一個tail節點就能解決這個問題。這樣時間複雜度就爲O(1)了。

    public void add(T val)
    {
        if(head==null)
        {
            head = new Node(val);
            tail = head;
        }
        else
        {
            tail.next = new Node(val);
            tail = tail.next;
        }
        ++size;
    }

下面是LinkedList的簡單實現。

LinkedList的優點: 新項的插入和現有項的刪除花費常數時間。(前提是已有變動項的位置,不然還是要在索引上花費O(N) )
LinkedList的缺點: 很難索引現有項,對get操作的調用花費很多時間。

MyLinkedList作爲雙向鏈表,get()方法可以從頭結點或者尾節點開始。所以最壞的情況下需要遍歷 【N/2】 個元素。

代碼中changedTimes和itChangedTimes的作用: 如果獲取了一個迭代器之後修改它所對應的MyLinkedList對象,則讓該迭代器失效。

package list;

import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
 * MyLinkedList
 * @author earayu
 * @param <T>
 */
public class MyLinkedList<T> implements Iterable<T> {
    //雙向鏈表的節點
    private static class Node<T>{
        public T data;
        public Node<T> prevNode;
        public Node<T> nextNode;
        public Node(T data, Node<T> prevNode, Node<T> nextNode) {
            this.data = data;
            this.prevNode = prevNode;
            this.nextNode = nextNode;
        }
    }
    //定義鏈表的長度,修改次數,頭結點和尾節點。
    private int size;
    private int changedTimes;
    private Node<T> firstNode;
    private Node<T> lastNode;
    /**
     * 調用init()方法,初始化鏈表
     */
    public MyLinkedList(){
        init();
    }
    /**
     * 初始化MyLinkedList,將長度,被改變次數,頭結點和尾節點都初始化。以免發生錯誤。
     */
    private void init(){
        size = 0;
        changedTimes = 0;
        firstNode = new Node<T>(null, null, null);
        lastNode = new Node<T>(null, null, null);
        firstNode.nextNode = lastNode;
        lastNode.prevNode = firstNode;
    }
    /**
     * 將鏈表清空,修改次數+1
     */
    public void clear(){
        size = 0;
        changedTimes++;
        firstNode = new Node<T>(null, null, null);
        lastNode = new Node<T>(null, null, null);
        firstNode.nextNode = lastNode;
        lastNode.prevNode = firstNode;
    }

    public boolean isEmpty(){
        return size() == 0;
    }

    /**
     * 返回當前鏈表長度(不含頭尾節點)
     * @return
     */
    public int size(){
        return size;
    }
    /**
     * 將idx索引出節點的值設置成newData
     * @param idx
     * @param newData
     * @return
     */
    public T set(int idx, T newData){
        Node<T> p = getNode(idx);
        T oldVal = p.data;
        p.data = newData;
        return oldVal;
    }
    /**
     * 刪除制定索引出的節點,索引從0開始
     * @param idx
     * @return
     */
    public T remove(int idx){
        return remove(getNode(idx));
    }

    private T remove(Node<T> p){
        p.nextNode.prevNode = p.prevNode;
        p.prevNode.nextNode = p.nextNode;
        --size;
        ++changedTimes;
        return p.data;
    }

    /**
     * 將data添加到鏈表尾部。
     * @param data
     * @return
     */
    public boolean add(T data){
        add(size, data);
        return true;
    }
    /**
     * 將鏈表添加到idx索引出,索引從0開始
     * @param idx
     * @param data
     * @return
     */
    public boolean add(int idx, T data){
        if(idx < 0 || idx > size){
            throw new IndexOutOfBoundsException();
        }
        Node<T> beforeNode = firstNode;
        for(int i=0;i<size;i++){
            beforeNode = beforeNode.nextNode;
        }
        Node<T> afterNode = beforeNode.nextNode;
        beforeNode.nextNode = new Node(data, beforeNode, afterNode);
        afterNode.prevNode = beforeNode.nextNode;
        ++size;
        ++changedTimes;
        return true;
    }
    /**
     * 獲取idx索引出的節點的數據,索引從0開始
     * @param idx
     * @return
     */
    public T get(int idx){
        return getNode(idx).data;
    }
    /**
     * 獲取idx索引出的節點
     * @param idx
     * @return
     */
    private Node<T> getNode(int idx){
        if(idx < 0 || idx >= size){
            throw new IndexOutOfBoundsException();
        }
        Node<T> p;
        if(idx < size/2){
            p = firstNode.nextNode;
            for(int i=0; i<idx; i++){
                p = p.nextNode;
            }
        }else{
            p = lastNode;
            for(int i=size; i>idx; i--){
                p = p.prevNode;
            }
        }
        return p;
    }

    /**
     * 返回一個迭代器
     */
    @Override
    public Iterator<T> iterator() {
        return new LinkedListIterator();
    }
    /**
     * 
     * @author earayu
     *
     */
    private class LinkedListIterator implements Iterator<T>{
        //迭代器的當前指向節點,修改次數,可否移除
        private Node<T> currentNode = firstNode.nextNode;
        private int itChangedTimes = changedTimes;
        private boolean removable = false;

        /**
         * 返回是否有下一個節點(不包含尾節點)。
         */
        @Override
        public boolean hasNext() {
            return currentNode != lastNode;
        }
        /**
         * 返回當前指向節點的下一個節點的值,並將指向的節點後移一位。
         */
        @Override
        public T next() {
            if(changedTimes != itChangedTimes){
                throw new ConcurrentModificationException();
            }
            if(!hasNext()){
                throw new NoSuchElementException();
            }

            T nextItem = currentNode.data;
            currentNode = currentNode.nextNode;
            removable = true;
            return nextItem;
        }
        /**
         * 調用外部類的remove方法,移除上一個訪問過的節點。即,當前指向節點的前一個節點。
         */
        public void remove(){
            if(changedTimes != itChangedTimes){
                throw new ConcurrentModificationException();
            }
            if(!removable){
                throw new IllegalStateException();
            }

            MyLinkedList.this.remove(currentNode.prevNode);
            removable = false;
            ++itChangedTimes;
        }
    }
}
發佈了28 篇原創文章 · 獲贊 4 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章