Java单链表的简单实现

一、数组的链式描述
在链式描述中,数据对象实例的每一个元素都用一个单元或结点来描述。结点不必是数据成员,因此不是用公式来确定元素位置。取而代之的是,每一个结点都包含另一个相关结点的位置信息,这个信息称为链或指针。
设L=(e0,e1,e2,…,en-1)是一个线性表。在这个表的链式描述中,每个元素都在一个单独的结点中描述,每一个结点都有它的链域,它的值是线性表的下一个元素的地址。这样一来,元素ei的结点链接这ei+1的结点。元素en-1的结点没有其他结点来可链接,因此链域值是NULL。
在这里插入图片描述

上图是线性表L的链式描述。链域用箭头表示。为了确定元素e2的位置,必须从firstNode开始,从其中的链域找到e1结点的指针,再从结点的链域找到e2结点的指针。一般来说,为了找到索引为theIndex的元素,需要从firstNode开始,跟踪theIndex个指针才能找到。
要删除下图中的元素e2,需要以下步骤(注意,e2是单链表中的第三个结点):
·找到第二个结点
·把第二个结点和第四个结点链接起来。
在这里插入图片描述

注意,删除了上图第三个结点,其后续结点的索引自动减一。链表的结点都是从firstNode开始。沿着一系列指针可以找到结点,而应该被删除的结点,从firstNode开始是不可能找到的,因此它不再是链表的结点,也就不用去修改它的指针域。
为了在链表中插入一个索引为index的结点,需要首先找到索引为index-1的结点,然后在它后面插入新结点。下图显示了在两种情况下(index=0和0<index≤listSize)进行删除时的指针变化
在这里插入图片描述
二、Java语言实现

package JavaProject.MyList;
//单链表
class Node<T> {//结点结构
    T data;
    Node<T> next;

    public Node() {         //默认无参构造方法
        this.next = null;
    }

    public Node(T element) {//给结点赋值的构造方法
        this.data = element;
        this.next = null;
    }

    public Node(T element, Node<T> next) {//给结点赋值,给next指向
        this.data = element;
        this.next = next;
    }
}

public class LinkedList<T> {
    private int size;
    private Node<T> firstNode = new Node<>();
    private Node<T> tail = new Node<>();

    public LinkedList() { //头尾不赋值
        size = 0;
        firstNode.next = tail;
    }

    public LinkedList(T[] a) { //通过数组转化成链表
        size = 0;
        firstNode.next = tail;
        for (int i = 0; i < a.length; i++) {
            this.add(a[i]);
        }
    }


    //下面的theIndex都是从0开始
    public void insert(T element, int theIndex) {  
        if (theIndex > size) {
            try {
                throw new OutOfBoundsError();
            } catch (OutOfBoundsError e) {
                e.printStackTrace();
            }
            return;
        } else {
            Node<T> p = firstNode;
            for (int i = 0; i <= theIndex - 1; i++) {//找要插入的前一个结点
                p = p.next;
            }
            p.next = new Node<T>(element, p.next); //插入目标结点
        }
        size++;
    }

    public void add(T element) {
        insert(element, size);
    }

    public void delete(int theIndex) {  //
        if (theIndex > size) {
            try {
                throw new OutOfBoundsError();
            } catch (OutOfBoundsError e) {
                e.printStackTrace();
            }
            return;
        } else {
            Node<T> p = firstNode;
            for (int i = 0; i <= theIndex - 1; i++) {//移到要删除的前一个结点
                p = p.next;
            }
            p.next = p.next.next;
        }
        size--;
    }

    public T get(int theIndex) { //index从0开始
        T element = null;
        Node<T> p = firstNode;
        if (theIndex >= size) {
            try {
                throw new OutOfBoundsError();
            } catch (OutOfBoundsError e) {
                e.printStackTrace();
            }
            return null;
        }
        for (int i = 0; i <= theIndex; i++) {
            p = p.next;
        }
        element = p.data;
        return element;
    }

    public void set(int theIndex, T element) {
        if (theIndex >= size) {
            try {
                throw new OutOfBoundsError();
            } catch (OutOfBoundsError e) {
                e.printStackTrace();
            }
        }
        Node<T> p = firstNode;
        for (int i = 0; i <= theIndex; i++) { //找到对应的索引结点
            p = p.next;
        }
        p.data = element;
    }
    
    public int indexOf(T element) {
        int theIndex = -1;
        if (!isEmpty()) {
            Node<T> p = firstNode;
            for (int i = 0; i < size; i++) {
                p = p.next;
                if (p.data.equals(element)) {
                    theIndex = i;
                    break;
                }
            }
        }
        return theIndex;
    }

    public void print() {
        Node<T> p = firstNode;
        for (int i = 0; i < size; i++) {
            p = p.next;
            System.out.print(p.data + " ");
        }
        System.out.println();
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        if (size == 0) {
            return true;
        } else {
            return false;
        }
    }

    public void mergList(LinkedList<T> listB) {//合并两个链表
        Node<T> p = this.firstNode;
        for (int i = 0; i < size; i++) { //找到A表的最后一个元素结点
            p = p.next;
        }
        p.next = listB.firstNode.next;
        size = this.size + listB.size;
        return ;
    }

}

说明:这个类包含了几个方法:

  • 两个构造方法:一个是无参但有方法体的,将size初始化为0,并将头结点指向尾结点,但是首尾结点均不赋值,仅作为标记。另一个是把传入的数组参数转化成单链表。
  • insert方法,用于在指定索引处插入指定元素,要先找到要插入位置的前一个结点,再进行插入操作
  • add方法,直接在列表尾部插入元素
  • delete方法:删除指定索引处的元素
  • get方法:获取指定索引处的元素
  • set方法:将指定索引处的旧值换成新值
  • indexOf方法:返回指定元素的索引,如果有多个,则返回第一个出现的索引
  • print方法:按顺序输出链表元素
  • size方法:返回链表的当前大小
  • isEmpty方法:判断链表是否为空
  • mergeList方法:将两个链表合并。方法是将第一个表的最后一个存放元素的结点(尾结点的前一个结点)指向第二个表的第一个存放元素的结点(头结点的下一个结点)

三、顺序结构和链式结构的优缺点

1.顺序表存储
存取速度高效,通过下标来直接存储。但是插入和删除比较慢,有时要扩容,插入或者删除一个元素时,整个表需要遍历移动元素。

2.链表存储
优点:插入和删除速度快,保留原有的物理顺序,插入或者删除一个元素时,只需要改变指针指向即可 。但是查找速度慢,因为查找时,需要从头结点开始逐个访问。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值