数据结构基础java实现—双向链表

来源:互联网 发布:tgp腾讯游戏客户端mac 编辑:程序博客网 时间:2024/05/22 13:37

设计双向链表需要两个类

1. LinkedList类,包含头结点first,尾节点last,节点个数size,记录头结点和尾节点有助于理解和简化编程。提供最基本的方法为:在指定索引位置插入和删除数据。

2. Node类,包含一个泛型对象的引用item,一个前引用prev,一个后引用next,这个可以设置为内部静态类。


在指定索引位置插入和删除数据,需要通过node(int index)方法返回index位置上的Node数据一遍操作。在返回index位置上的node数据时,要考虑index与size的关系,从而判别是从前往后查找还是从后往前查找。

插入和删除数据时需要分三种情况考虑:头结点插入删除,尾节点插入删除,中间节点插入删除。每种情况均写成私有的方法,由public方法根据实际情况调用。

在public的方法中,凡是牵扯的index的操作都要对index做检查,防止下标越界,这个由checkIndex(int index)方法完成。需要注意的是插入数据时的下标检查与其他时候的下标检查不一致,因为插入数据时index是可以等于size的。

以下代码为个人所写代码:

package wenpq.util;/** * @author wenpq * wenpq的linkedList */public class LinkedList<E>{private int size = 0;private Node<E> first; // 头结点与尾节点的使用,排除了特殊情形简化编码private Node<E> last;// ------------------------------------------------构造函数public LinkedList() {}// ------------------------------------------------内部使用函数// 检查下标是否越界    private void checkIndex(int index) {        if (!(index >= 0 && index < size))            throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);    }    // 返回index位置上的node    Node<E> node(int index) {    // 右移1 和除2取整等价    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;    }    }        // 头节点前插入void linkFirst(E e){Node<E> f = first;Node<E> newNode = new Node<>(null,e,f);if (f==null){last = newNode;}else{f.prev = newNode;}first = newNode;size++;}// 尾节点后插入void linkLast(E e){Node<E> l = last;Node<E> newNode = new Node<>(l,e,null);if(l==null){first = newNode;}else{l.next = newNode;}last = newNode;size++;}// 中间前节点插入  插入后 element位置就是index 故要插入原来位置之前void linkBefore(E e, Node<E> succ){Node<E> newNode = new Node<>(succ.prev,e,succ);succ.prev.next = newNode;succ.prev = newNode;size++;}// 头节点删除    void unlinkFirst(Node<E> f) {        final Node<E> next = f.next;        f.item = null;        f.next = null; // help GC        first = next;        if (next == null)            last = null;        else            next.prev = null;        size--;    }    // 尾节点删除    void unlinkLast(Node<E> l) {        final Node<E> prev = l.prev;        l.item = null;        l.prev = null; // help GC        last = prev;        if (prev == null)            first = null;        else            prev.next = null;        size--;    }    // 中间节点删除    void unlink(Node<E> x) {        final Node<E> next = x.next;        final Node<E> prev = x.prev;                prev.next = next;        x.prev = null;        next.prev = prev;        x.next = null;        x.item = null;        size--;    }// ------------------------------------------------可以继承自接口的一些操作(没有写完)public int size() {return size;}public boolean add(E e) {linkLast(e);return true;}public E get(int index) {checkIndex(index);        return node(index).item;}public boolean set(int index, E element) {checkIndex(index);        Node<E> x = node(index);        x.item = element;        return true;}public void add(int index, E element) {// 注意在add的时候index是可以等于size的        if (!(index >= 0 && index <= size))            throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);        if (index == size){        linkLast(element);        }else{        if(index ==0 ){        linkFirst(element);        }else                 linkBefore(element, node(index));        }       }public void remove(int index) {checkIndex(index);        if (index == size-1){        unlinkLast(node(index));        }else{        if(index ==0 ){        unlinkFirst(node(index));        }else         unlink(node(index));        }  }/** * 采用静态内部类 减少代码 较少动态创建开销 */private static class Node<E> {// 一个E类型引用// 一个前引用// 一个后引用Node<E> prev;E item;Node<E> next;public Node(Node<E> prev, E item, Node<E> next) {super();this.prev = prev;this.item = item;this.next = next;}}}

以下为测试代码:

public class Test {public static void main(String[] args) {       LinkedList<String>  list = new LinkedList<String>();                  list.add("one");          list.add("two");          list.add("three");                          list.add(0,"first");          list.add(list.size(),"end");                  list.set(1, "new one");        for(int i=0;i<list.size();i++){        System.out.println(list.get(i));        }                list.remove(3);        list.remove(3);                for(int i=0;i<list.size();i++){        System.out.println(list.get(i));        }}}



对比util的LinkedList的代码,标准库则做了更多考虑,在类申明时:

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

之所以继承AbstractSequentialList ,是因为它实现了get(int index)、set(int index, E element)、add(int index, E element) 和 remove(int index)这些骨干性函数。降低了List接口的复杂度。这些接口都是随机访问List的,LinkedList是双向链表;既然它继承于AbstractSequentialList,就相当于已经实现了“get(int index)这些接口”。

java.io.Serializable用于声明该类的对象可以序列化,在socket通信和远程方法调用时会用到。实际上在源代码中头结点first,尾节点last,节点个数size这些都被transient关键字修饰,限定了不可能序列化的属性。

而在完成get,set,add方法时也不像上面代码那样分三种情况讨论,因为它调用的内部方法已经做了考虑。

对于remove方法,util源码返回了remove的对象,可能是因为考虑到同时要取数据燃耗删除数据的操作吧,目前还明白为什么要这么做,先留着,以后再来想。

0 0
原创粉丝点击