由于单链表是由一个个结点链接而成,所以一下用单链表结点类和单链表类描述单链表
1.单链表结点类
采用java语言的引用类型来实现链式存储结构,单链表结点类Node声明如下:
package linearList;
public class Node<E> {//单链表结点类
public E data;//数据域
public Node<E> next;//地址域
/*
* 构造结点
*/
public Node(E data,Node<E> next){
this.data = data;
this.next = next;
}
public Node(E data){
this(data,null);
}
public Node(){
this(null,null);
}
}
2.创建结点
Node类的一个对象表示链表中的一个结点,通过next将两个结点“链接”起来。如图
建立并链接两个结点的语句如下
Node<String> p,q;//声明p、q是结点对象
p = new Node<String>("A");//创建一个结点,由P引用
q = new Node<String>("B");
p.next = q;//链接,使q结点成为p结点的后继结点
或者
Node<String> q = new Node<String>("B");
Node<String> p = new Node<String>("A",q);//构造结点,指定数据元素和后继结点
若干个结点通过next链指定相互之间的顺序关系,形成一条单链表。为了方便更改结点间的连接关系,Node类中成员变量被声明成public的,允许其他类访问。如果将其声明为private的,就必须提供一组public的set、get方法,此处讨论的重点是链表,故其他方面比较简洁。
单链表的头指针也是一个结点引用,即地址域,声明如下:
Node<E> head = null;
当head==null时,表示空单链表。
3.单链表的 遍历操作
遍历单链表是指从第一个结点开始,沿着结点链接的指向,依次访问单链表中的每个结点,并且每个结点只被访问一次。,如下图所示
遍历单链表操作不应该改变头指针head,因此需要声明一个变量p指向当前结点。只要涉及到遍历或者循环,大多数时候都是把当前变量改变之后再赋值给当前变量,算法描述如下:
Node<E> p = head;//p从head指向的结点开始
while(p!=null){
访问p结点;
p = p.next;//p到达后继结点
}
4.单链表的插入操作
根据插入的位置不同,分为下列4种情况
(1)空表插入/头插入
这两种情况都会改变头指针head,算法如下
if(head==null){
head = Node<E>(x);
}else{
Node<E> q = new Node<E>(x);
q.next = head;
head = q;
}
(2)中间插入/尾插入
这两种情况都不会改变头指针head,算法如下
Node<E> q = new Node<E>(x);
q.next = p.next;
p.next = q;
5.单链表的删除操作
根据插入的位置不同,分为下列2种情况
(1)头删除
删除单链表第一个结点,只要使head指向其后继结点即可,如下:
head = head.next;
如果单链表只有一个结点的话,执行此语句之后,单链表为空,head为null
(2)中间/尾删除
if(p.next!=null)
p.next = p.next.next;
从上述讨论我们可以看到,对当单链表进行插入和删除操作时不需要移动数据元素,只需要改变某些结点的链接,操作效率较高,后面我们再做分析。