duan2

导航

 

  链表是一系列的存储数据元素的单元通过指针串接起来形成的,因此每个单元至少有两
个域,一个域用于数据元素的存储,另一个域是指向其他单元的指针。这里具有一个数据域
和多个指针域的存储单元通常称为 结点(node)

  在 Java 中没有显式的指针类型,然而实际上对象的访问就是使用指针来实现的,即在
Java 中是使用对象的引用来替代指针的。因此在使用 Java 实现该结点结构时,一个结点本
身就是一个对象。结点的数据域 data 可以使用一个 Object 类型的对象来实现,用于存储任
何类型的数据元素,并通过对象的引用指向该元素;而指针域 next 可以通过节点对象的引
用来实现。
  由于数据域存储的也是对象引用,因此数据实际上和图 3-2 中一样,是通过指向数据的
物理存储地址来完成存储的,但是在后面叙述的方便,我们在图示中都将数据元素直接画到
了数据域中,请读者注意实际的状态与之是有区别的。

 

 


  上面的单链表结点结构是结点的一种最简单的形式,除此之外还有其他不同的结点结
构,但是这些结点结构都有一个数据域,并均能完成数据元素的存取。为此在使用 Java 定
义单链表结点结构之前先给出一个结点接口,在接口中定义了所有结点均支持的操作,即对
结点中存储数据的存取。

节点接口

public interface Node {
    //获取结点数据域
    public Object getData();
    //设置结点数据域
    public void setData(Object obj);
}

 

单链表节点

public class SLNode implements Node {
    private Object element;
    private SLNode next;
    public SLNode() {
        this(null,null);
    }
    public SLNode(Object ele, SLNode next){
        this.element = ele;
        this.next = next;
    }
    public SLNode getNext(){
        return next;
    }
    public void setNext(SLNode next){
        this.next = next;
    }
    /**************** Methods of Node Interface **************/
    public Object getData() {
        return element;
    }
    public void setData(Object obj) {
        element = obj;
    }
}

 

单链表实现

public class ListSLinked implements List{
    private SLNode head; //单链表首结点引用
    private int size; //线性表中数据元素的个数
    public ListSLinked () {
        head = new SLNode();
        size = 0;
    }
    //辅助方法:获取数据元素 e 所在结点的前驱结点
    private SLNode getPreNode(Object e){
        SLNode p = head;
        while (p.getNext()!=null)
            if (p.getNext().getData().equals(e)) return p;
            else p = p.getNext();
        return null;
    }
    //辅助方法:获取序号为 0<=i<size 的元素所在结点的前驱结点
    private SLNode getPreNode(int i){
        SLNode p = head;
        for (; i>0; i--) p = p.getNext();
        return p;
    }
    //获取序号为 0<=i<size 的元素所在结点
    private SLNode getNode(int i){
        SLNode p = head.getNext();

        for (; i>0; i--) p = p.getNext();
        return p;
    }
    //返回线性表的大小,即数据元素的个数。
    public int getSize() {
        return size;
    }
    //如果线性表为空返回 true,否则返回 false。
    public boolean isEmpty() {
        return size==0;
    }
    //判断线性表是否包含数据元素 e
    public boolean contains(Object e) {
        SLNode p = head.getNext();
        while (p!=null)
            if (p.getData().equals(e)) return true;
            else p = p.getNext();
        return false;
    }
    //返回数据元素 e 在线性表中的序号
    public int indexOf(Object e) {
        SLNode p = head.getNext();
        int index = 0;
        while (p!=null)
            if (p.getData().equals(e)) return index;
            else {index++; p = p.getNext();}
        return -1;
    }
    //将数据元素 e 插入到线性表中 i 号位置
    public void insert(int i, Object e) throws IndexOutOfBoundsException {
        if (i<0||i>size)
            throw new IndexOutOfBoundsException("错误,指定的插入序号越界。");
        SLNode p = getPreNode(i);
        SLNode q = new SLNode(e,p.getNext());
        p.setNext(q);
        size++;
        return;
    }



    //删除线性表中序号为 i 的元素,并返回之
    public Object remove(int i) throws IndexOutOfBoundsException {
        if (i<0||i>=size)
            throw new IndexOutOfBoundsException("错误,指定的删除序号越界。");
        SLNode p = getPreNode(i);
        Object obj = p.getNext().getData();
        p.setNext(p.getNext().getNext());
        size--;
        return obj;
    }
    //删除线性表中第一个与 e 相同的元素
    public boolean remove(Object e) {
        SLNode p = getPreNode(e);
        if (p!=null){
            p.setNext(p.getNext().getNext());
            size--;
            return true;
        }
        return false;
    }

    //返回线性表中序号为 i 的数据元素
    public Object get(int i) throws IndexOutOfBoundsException {
        if (i<0||i>=size)
            throw new IndexOutOfBoundsException("错误,指定的序号越界。");
        SLNode p = getNode(i);
        return p.getData();
    }
}

 

基于时间的比较
  线性表的操作主要有查找、插入、删除三类操作。
对于查找操作有基于序号的查找,即存取线性表中 i 号数据元素。由于数组有随机存取
的特性,在线性表的顺序存储实现中可以在Θ(1)的时间内完成;而在链式存储中由于需要
从头结点开始顺着链表才能取得,无法在常数时间内完成,因此顺序存储优于链式存储。查
找操作还有基于元素的查找,即线性表是否包含某个元素、元素的序号是多少,这类操作线
性表的顺序存储与链式存储都需要从线性表中序号为 0 的元素开始依次查找,因此两种实现
的性能相同。综上所述,如果在线性表的使用中主要操作是查找,那么应当选用顺序存储实
现的线性表。
对于基于数据元素的插入、删除操作而言,当使用数组实现相应操作时,首先需要采用
顺序查找定位相应数据元素,然后才能插入、删除,并且在插入、删除过程又要移动大量元
素;相对而言链表的实现只需要在定位数据元素的基础上,简单的修改几个指针即可完成,
因此链式存储优于顺序存储。对于基于序号的插入、删除操作,因为在顺序存储中平均需要
移动一半元素;而在链式存储中不能直接定位,平均需要比较一半元素才能定位。因此顺序
存储与链式存储性能相当。综上所述,如果在线性表的使用中主要操作是插入、删除操作,
那么选用链式存储的线性表为佳。

 

posted on 2020-04-26 20:59  duan2  阅读(611)  评论(0编辑  收藏  举报