单链表的概念:
采用链式存储方式存储的线性表称之为链表,链表中每个节点包含存放数据元素的值的数据域和存放指向逻辑上相邻节点的指针域。若一个节点中只包含一个指针域,则称此链表为单链表。
单链表的特点:
1. 单链表是通过指向后继节点的指针把它的一串节点连接成一个链
2. 以线性表这种第一个数据元素的存储地址作为线性表的起始地址,称做线性表的头指针。一个单链表就是由他的头指针head来唯一的标识他。
3. 单链表中的最后一个节点(尾节点)没有后继,所以它的指针域的值用空指针null。
4. 为了操作方便,在第一个存入数据的节点之前加入一个虚节点称为“头节点”,头结点的数据域一般不存放具体的值,指针域存放指向第一个节点(首节点)的指针,指向头节点的指针为单链表的头指针。
5. 若线性表为空表,则头指针的指针域为空
单链表的节点类的相关代码(java):
class Node<T>
{
public T data;//用于存储相关的数据域
public Node<T> next;//用于存储相关的指针域
public Node()
{
this(null,null);
}
public Node(T data)
{
this(data,null);
}
public Node(T data,Node next)
{
this.data=data;
this.next=next;
}
}
单链表实现的相关代码(java):
public class LinkedList<T> implements List<T>
{
public Node head;//用于存储头指针
public LinkedList()
{
head=new Node();
}
public void clear()//清空,时间复杂度为O(1)
{
head=new Node();
}
public boolean isEmpty()//判空操作,时间复杂度为O(1)
{
return head.next==null;
}
public int length()//求长度
{
int k=0;
Node p=head.next;
while(p!=null)
{
k++;
p=p.next;
}
return k;
}
public T get(int i)throws Exception//按位查找,其时间复杂度为O(n)
{
int j=0;
Node p=head.next;
while(j<i&&p!=null)
{
j++;
p=p.next;
}
if(j>i||p==null)
{
throw new Exception(“查找的位置不存在”);
}
return p.data;
}
public int indexOf(T x)//按值查找,其时间复杂度为O(n)
{
int j=0;
Node p=head.next;
while(p!=null&&!p.data.equals(x))
{
j++;
p=p.next;
}
if(p==null)
return -1;
else
return j;
}
public void insert(int i,T x)throws Exception//插入操作,其时间复杂度为O(n)
{
int j=-1;
Node p=head;
while(j<i-1&&p!=null)//用于找到插入位置的前一个节点
{
j++;
p=p.next;
}
if(i-1<j||p==null)
{
throw new Exception(“插入的位置不合法”);
}
Node s=new Node(x);
s.next=p.next;
p.next=s;
}
public void remove(int i)throws Exception//删除操作,时间复杂度为O(n)
{
int j=-1;
Node p=head;
while(j<i-1&&p.next!=null)
{
j++;
p=p.next;
}
if(j>i-1||p.next!=null)
{
throw new Exception(“删除的位置不合法”);
}
p.next=p.next.next;
}
}
ps:对于单链表的删除操作而言,其必须要找到进行删除的节点的前继节点,之后才能进行删除。删除某个特定节点的另一个思路是,将下一个节点的数据域的值赋值给本节点的数据域,之后,删除通过该特定节点,去删除下一个节点。这样做的局限在于,无法删除单链表中的最后一个节点。
其它链表:
除了单链表之外,还有单链表的改进和结构的调整,常见的有循环链表和双向链表以及双向循环链表。
ps:对于“链表”该名词,有两种含义,一种是指单链表,一种是指各种形式的链式存储结构。为此,“链表”的具体含义应该取决于上下文。
单向循环链表:
循环链表也称为环形链表,其结构和单链表相似,只是将单链表的首尾相连,即将单链表的最后一个节点的后继指针指向第一个节点,从而构成一个环形链表
在循环链表中,每一个节点都有后继,所以,从循环链表的任意一个节点出发都可以访问到单链表中的所有节点。
对于单链表和循环链表,其API的实现上大多数没有什么区别,仅有的区别在于判断是否为最后一个节点的时候是否为头结点,而不是单链表实现上的最后一个节点为null。
在实现循环链表时,既可以用头指针来标识循环链表,也可以用尾指针(所谓的尾指针,指的是指向末尾节点的指针)来标识,也可以头尾指针都用。若仅用头指针标识循环链表,则访问第一个节点的时间复杂度为O(1),但是访问最后一个节点的时间复杂度为O(n);若仅用尾指针标识的循环链表,则不论是访问第一个节点还是访问最后一个节点,其时间复杂度为O(1),所以,在实际操作中,使用尾指针来标识循环链表,这样会简化某些操作
双向链表
在单链表中的节点仅仅包含指向其后继节点的指针,所以要查找一个指定节点的后继节点,只要顺着其后继指针即可找到,其时间复杂度为O(1),但是,若其要找到某个指定节点的前驱节点,则其需要从单链表的表头开始顺着链依次进行查找,其时间复杂度为O(n),为了解决这个问题,让每一个节点都有两个指针域,一个指向前驱节点,一个指向后继节点,此种链表就称为双向链表。
双向循环链表
将双向链表的首尾进行相连即可构成双向循环链表
双向链表的节点插入及删除操作图
插入:
删除: