JAVA数据结构-----单链表

线性表的链式存储结构是指:用一组任意的存储单元(可以连续,也可以不连续)存储线性表中的数据单元。即逻辑上相邻的数据在计算机内的存储位置不一定相邻。如图所示:

    

这里具有一个数据域和多个指针域的存储单元通常称为 结点(node)。

在Java中没有显式的指针类型。然而实际上对象的访问就是使用指针来实现的,即在Java 中是使用对象的引用来替代指针的。

因此在使用 Java 实现该结点结构时,一个结点本身就是一个对象。我们定义一个先结点,为了提高复用性,在接口中定义了所有结点均支持的操作,即对结点中存储数据的存取。

/**
 * Created by Troshin on 2018/1/14.
 */
public interface Node {
    //获取结点数据域
    public Object getData();

    //设置结点数据域
    public void setData(Object obj);
}
单链表结点定义:

/**
 * Created by Troshin on 2018/1/14.
 */
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;
    }

    /**
     * 接口实现
     * @return
     */
    @Override
    public Object getData() {
        return element;
    }

    @Override
    public void setData(Object obj) {
        this.element=obj;
    }
}
单链表是通过上述定义的结点使用 next 域依次串联在一起而形成的。


链表的第一个结点和最后一个结点,分别称为链表的 首结点和 尾结点。

尾结点的特征是其 next 引用为空(null),"^"表示"null"的意思。

与数组类似,单链表中的结点也具有一个线性次序,即如果结点 P 的 next 引用指向结点 S,则 P 就是 S 的直接前驱,S 是 P 的直接后续。单链表的一个重要特性:就是只能通过前驱结点找到后续结点,而无法从后续结点找到前驱结点。

我们先看看图解的查找、插入、删除操作:




除了查找操作外,插入和删除都给了三个不一样的方法,因此它们的时间复杂度也不一样,复杂度我们稍后讨论。现在我们看看方法的实现,方法的定义在上一章节,因此这里就不重复写了。

/**
 * Created by Troshin on 2018/1/14.
 */
public class ListSLinked implements myList {
    private Strategy strategy;      //数据元素比较策略
    private SLNode head;            //单链表首结点引用
    private int size;               //线性表中的数据元素的个数

    /**
     * 构造方法
     */
    public ListSLinked(){
        this(
                new DefaultStrategy());
    }

    public ListSLinked(Strategy strategy){
        this.strategy=strategy;
        head=new SLNode();
        size=0;
    }
    /**
     * 辅助方法:获取数据元素 e 所在结点的前驱结点
     */
    private SLNode getPreNode(Object e){
        SLNode p=head;
        while (p.getNext()!=null)
            if(strategy.equal(p.getNext().getData(),e))
                return  p;
            else p=p.getNext();
        return null;
    }

    /**
     * 辅助方法:获取序号为 0<=i<size 的元素所在结点的前驱结点
     * @return
     */
    private SLNode getPreNode(int i){
        SLNode p =head;
        for (;i>0;i--)
            p=p.getNext();
        return p;
    }

    /**
     * 获取序号为 0<=i<size 的元素所在结点
     * @param i
     * @return
     */
    private SLNode getNode(int i){
        SLNode p=head.getNext();
        for (;i>0;i--)
            p=p.getNext();
        return p;
    }

    /**
     * 返回线性表的大小,即数据元素的个数。
     * @return
     */
    @Override
    public int getSize() {
        return size;
    }

    /**
     * 如果线性表为空返回 true,否则返回 false。
     * @return
     */
    @Override
    public boolean isEmpty() {
        return size==0;
    }

    /**
     * 判断线性表是否包含数据元素 e
     * @param e
     * @return
     */
    @Override
    public boolean contains(Object e) {
        SLNode p=head.getNext();
        while (p!=null)
            if (strategy.equal(p.getData(),e))
                return true;
            else p=p.getNext();
        return false;
    }

    /**
     * 返回数据元素 e 在线性表中的序号
     * @param e
     * @return
     */
    @Override
    public int indexOf(Object e) {
        SLNode p=head.getNext();
        int index=0;
        while (p!=null)
            if (strategy.equal(p.getData(),e))
                return index;
            else {
                index ++;
                p=p.getNext();
            }
        return -1;
    }

    /**
     * 将数据元素 e 插入到线性表中 i 号位置
     * @param i
     * @param e
     * @throws OutOfBoundaryException
     */
    @Override
    public void insert(int i, Object e) throws OutOfBoundaryException {
        if (i<0||i>size)
            throw new OutOfBoundaryException("错误,指定插入的序号越界");
        SLNode p=getPreNode(i);
        SLNode q=new SLNode(e,p.getNext());
        p.setNext(q);
        size ++;
        return;
    }

    /**
     * 将数据元素 e 插入到元素 obj 之前
     * @param obj
     * @param e
     * @return
     */
    @Override
    public boolean insertBefore(Object obj, Object e) {
        SLNode p =getPreNode(obj);
        if (p!=null) {
            SLNode q = new SLNode(e,p.getNext());
            p.setNext(q);
            size++;
            return true;
        }
        return false;
    }

    /**
     * 将数据元素 e 插入到元素 obj 之后
     * @param obj
     * @param e
     * @return
     */
    @Override
    public boolean insertAfter(Object obj, Object e) {
        SLNode p=head.getNext();
        while (p!=null)
            if (strategy.equal(p.getData(),obj)) {
                SLNode q = new SLNode(e, p.getNext());
                p.setNext(q);
                size ++;
                return true;
            }
            else p=p.getNext();
        return false;
    }

    /**
     * 删除线性表中序号为 i 的元素,并返回之
     * @param i
     * @return
     * @throws OutOfBoundaryException
     */
    @Override
    public Object remove(int i) throws OutOfBoundaryException {
        if (i<0||i>size)
            throw new OutOfBoundaryException("错误,指定删除的序号越界");
        SLNode p=getPreNode(i);
        Object obj=p.getNext().getData();
        p.setNext(p.getNext().getNext());
        size --;
        return obj;
    }

    /**
     * /删除线性表中第一个与 e 相同的元素
     * @param e
     * @return
     */
    @Override
    public boolean remove(Object e) {
        SLNode p=getPreNode(e);
        if (p!=null){
            p.setNext(p.getNext().getNext());
            size --;
            return true;
        }
        return false;
    }

    /**
     * 替换线性表中序号为 i 的数据元素为 e,返回原数据元素
     * @param i
     * @param e
     * @return
     * @throws OutOfBoundaryException
     */
    @Override
    public Object replace(int i, Object e) throws OutOfBoundaryException {
        if (i>0 || i>=size)
            throw new OutOfBoundaryException("错误,指定的序号越界");
        SLNode p=getNode(i);
        Object obj=p.getData();
        p.setData(e);
        return obj;
    }

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

方法 getSize()、isEmpty()的时间复杂度均为Θ(1)。通过成员变量 size 可以直接判断出线性表中数据元素的个数以及线性表是否为空。


方法 getPreNode(Object e)、getPreNode(int i),其功能是找到数据元素 e 或线性表中 i 号数据元素所在结点的前驱结点。这两个方法的平均运行时间 T(n)≈n/2。


方法 replace(int i, Object e)、get(int i)的平均运行时间 T(n)≈n/2,比使用数组实现相应操作要慢得多。


方法  contains(Object e)、indexOf(Object e)主要是在线性表中查找某个数据元素。方法平均运行时间与使用数组的实现一样,都需要从线性表中 0 号元素出发,依次向后查找,因此方法运行时间 T(n) ≈ n/2。


方法 insert(int i, Object e)、remove(int i)的运行时间 T(n)≈n/2,与使用数组实现的运行时间相同。


方法 insertBefore(Object obj, Object e)、insertAfter(Object obj, Object e)、remove(Object e)的平均运行时间 T(n)≈n/2 < n,要优于使用数组实现的运行时间。


数组,即顺序存储结构(顺序表)。



初入数据结构,以学习总结为主,如果写的不详细或者有什么其它错误的地方希望各位dalao们指点指点。

参考的书籍:《数据结构-JAVA版》《数据结构与算法》








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值