数据结构-单向链表
前言
在上一章节中,我们使用顺序存储结构实现了线性表,我们发现线性表的查询效率很快,时间复杂度为O(1),但是增添和删除数据的效率是比较低的。我们每一次增删操作都会造成大量数据的移动,基于这些问题,这篇文章将讨论用另一种存储结构实现线性表——链式存储结构。
一、什么链式存储结构?
链式存储结构是一种物理存储单元非连续,非顺序的存储结构,其数据元素的物理逻辑顺序是通过链表中的指针连接次序实现的。链表是由一系列节点组成,每个节点分为数据域(data)和指针域(next),数据域用来存储数据,指针域指向下一个节点,节点可以在运行时动态生成。(如图所示)
二、单向链表的实现思想
(这篇我们先来关注单向链表,双链表下一章再讨论)
插入数据:
删除数据:
三 、实现代码
package sequence;
import java.util.Iterator;
public class LinkList<T> implements Iterable<T>{
//记录头节点
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<T> 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我们通过内部类的方式实现,使用构造器对其成员变量进行初始化;提供遍历方式的实现在上一篇(数据结构-顺序表的实现)中已经进行详细说明(点此处查看)此处不再赘述。
总结
通过对顺序表和链表的讨论我们可以发现,链表的优点是:链表在内存中的位置不连续,能够按需申请和释放空间,不会造成空间浪费;其次对于数据的增删操作效率更高,不会像顺序表一样“牵一发而动全身”,但与此同时,链表的缺点就是:由于存储空间不连续,所以不能通过下标访问元素,查询效率低,二分法或排序等需要物理空间上连续的算法不再适用。