文章目录
- 数据结构-单向链表
- 前言
- 一、什么链式存储结构?
- 二、单向链表的实现思想
- 三 、实现代码
- 总结
前言
在上一章节中,我们使用顺序存储结构实现了线性表,我们发现线性表的查询效率很快,时间复杂度为O(1),但是增添和删除数据的效率是比较低的。我们每一次增删操作都会造成大量数据的移动,基于这些问题,这篇文章将讨论用另一种存储结构实现线性表——链式存储结构。
一、什么链式存储结构?
链式存储结构是一种物理存储单元非连续,非顺序的存储结构,其数据元素的物理逻辑顺序是通过链表中的指针连接次序实现的。链表是由一系列节点组成,每个节点分为数据域(data)和指针域(next),数据域用来存储数据,指针域指向下一个节点,节点可以在运行时动态生成。(如图所示)
(这篇我们先来关注单向链表,双链表下一章再讨论)
插入数据:
删除数据:
package sequence; import java.util.Iterator; public class LinkListimplements Iterable { //记录头节点 private Node head; //记录链表的长度 private int N; //头节点内部类 private class Node { //数据域 T item; //指针域 Node next; public Node(T item,Node next){ this.item = item; this.next = next; } } public LinkList(){ //初始化头节点 this.head = new Node(null,null); //初始化元素个数 this.N = 0; } //清空链表 public void clear(){ head.next = null; this.N = 0; } //获取链表长度 public int getLength(){ return N; } //判断链表是否为空 public boolean isEmpty(){ return this.N == 0; } //获取指定位置i处的元素 public T get(int i){ Node n = head.next; //通过循环,从头结点往后找,寻找第i次对应的元素 for (int index = 0; index < i; index++) { n = n.next; } return n.item; } //向链表中添加数据 public void insert(T t){ //找到当前元素的最后一个节点; Node n = head; while (n.next != null){ n = n.next; } //创建新的元素t和节点: Node newNode = new Node(t, null); //让当前最后一个元素节点指向新节点: n.next = newNode; //元素个数+1: N++; } //向链表第i个元素之前添加元素 public void insert(int i,T t){ //找到i位置前一个节点: Node pre = head; for(int index = 0;index < i ;index++){ pre = pre.next; } //找到i位置节点: Node curr = pre.next; //创建新节点,保存新元素t,并且使新节点的指针域指向i位置: Node newNode = new Node(t,curr); //使i位置前的节点指向新元素节点: pre.next = newNode; //元素个数+1: N++; } //删除并返回线性表中第i个元素 public T remove(int i){ //找到i位置的前一个节点: Node pre = head; for(int index = 0;index < i ;index++){ pre = pre.next; } //找到i位置的节点: Node curr = pre.next; //找到i位置的下一个节点: Node NeNode = curr.next; //使i位置的前一个节点指向i位置的后一个节点: pre.next = NeNode; //元素个数-1: N--; return curr.item; } //返回线性表中首次出现指定元素的索引 public int indexOf(T t){ //从头结点开始寻找: Node n = head; for (int index = 0;n.next != null;index++){ n = n.next; if(n.item.equals(t)){ return index; } } return -1; } //提供遍历方式: @Override public Iterator iterator() { return new LIterator(); } private class LIterator implements Iterator{ private Node n; public LIterator(){ this.n = head; } @Override public boolean hasNext() { return n.next != null; } @Override public Object next() { n = n.next; return n.item; } } }
说明:节点类Node我们通过内部类的方式实现,使用构造器对其成员变量进行初始化;提供遍历方式的实现在上一篇(数据结构-顺序表的实现)中已经进行详细说明(点此处查看)此处不再赘述。
总结
通过对顺序表和链表的讨论我们可以发现,链表的优点是:链表在内存中的位置不连续,能够按需申请和释放空间,不会造成空间浪费;其次对于数据的增删操作效率更高,不会像顺序表一样“牵一发而动全身”,但与此同时,链表的缺点就是:由于存储空间不连续,所以不能通过下标访问元素,查询效率低,二分法或排序等需要物理空间上连续的算法不再适用。



