数据结构: 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;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值