一 、链表的概念
集合的顺序存储是通过数组来实现的,集合的链式存储是通过存储结点之间的链接来实现的,从而形成链表。链表中的每一个结点都包含一个值域和一个指针(即引用对象域),每个结点的指针指向下一个结点对象,这样的链表称之为单链表。在单链表中,第一个结点称之为表头结点,指向第一个结点的指针被称为表头指针,最后一个结点称为表尾结点,表尾结点的指针为空。通常,为了方便插入和删除结点,通常在表头结点增加一个无值的结点,我们称之为附加头结点,用head表示,如链表为空,附加头结点的指针域指向自身,指针域为空,若链表不为空,则附加头结点的指针指向第一个结点。如下图所示。
二 、结点类的定义
我们定义类名为Node,数值域和指针域分别用element和next表示。该类包括两个构造方法,一个只带有结点引用对象参数,实现对当前next结点的赋值;另一个带有两个参数,分别实现对当前结点的值域element和指针域next的赋值。Node结点的定义如下:
public class Node {
Object element;
Node next;
public Node(Node next){
this.next = next;
}
public Node(Object element,Node next){
this.element = element;
this.next = next;
}
}
三 、具体操作的实现
1 、初始化集合,首先我们要定义附加头结点head为null和链表的长度length为0,然后再定义一个无参构造方法。
public class LinkedSet {
public Node head;
public int length;
public LinkedSet(){
head = new Node(null);
length = 0;
}
}
2 、向集合中添加一个元素
public boolean add(Object obj){
Node temp = head; //定义一个Node类型的临时变量,指向附加头结点
while(temp.next!=null){ //是否为链表最后的结点
if(temp.next.element.equals(obj)){ //判断链表中是否有与obj内容相同的结点
return false;
}else{
temp = temp.next; //如果不是,则取下一个结点
}
}
temp.next = new Node(obj,null); //temp为链表的最后一个结点,并在该结点后创建一个新的结点
length++; //链表的长度加1
return true;
}
3 、从集合中删除一个元素
public boolean remove(Object obj){
Node temp = head; //定义一个Node类型的临时变量,指向附加头结点
while(temp.next!=null){ //循环遍历单链表
if(temp.next.element.equals(obj)){ //判断是否存在相同的元素,若存在,则跳出循环
break;
}
temp = temp.next; //修改temp,指向后继结点
}
if(temp.next!=null){ //条件成立时,temp.next指要删除的结点
temp.next = temp.next.next; //从链表中删除该结点
length--; //链表长度减1
return true; //删除成功返回true
}
return false; //删除失败返回false
}
4 、判断一个元素是否属于该集合
public boolean contains(Object obj){
Node temp = head.next; //定义一个Node类型的临时变量,指向第一个结点
while(temp!=null){ //循环遍历单链表
if(temp.element.equals(obj)){ //判断是否存在相同的元素,若存在,则返回true
return true;
}
temp = temp.next; //修改temp,指向后继结点
}
return false; //若不存在,则返回false
}
5 、返回集合中第i个元素的值
public Object get(int index){
if(index<1||index>length){
System.out.println("该索引不在集合范围内");
System.exit(1); //结束该方法
}
Node temp = head; //定义一个Node类型的临时变量,指向附加头结点
int i = 1;
while(i<=index){ //定义一个变量,用来表示循环的次数
temp = temp.next; //修改temp,指向后继结点
i++; //i自动加1
}
return temp.element; //返回查找结点的内容
}
6 、从集合中按值查找元素
public Object find(Object obj){
Node temp = head.next; //定义一个Node类型的临时变量,指向第一个结点
while(temp!=null){ //循环遍历单链表
if(temp.element.equals(obj)){ //判断是否存在相同的元素,若存在,则返回obj
return obj;
}
temp = temp.next; //修改temp,指向后继结点
}
return null; //查找失败返回null
}
7 、返回集合的长度
public int size(){
return length;
}
8 、判断集合是否为空
public boolean isEmpty(){
return length==0;
}
9 、输出集合的所有元素
public void print(){
Node temp = head.next;
while(temp!=null){
System.out.print(temp.element.toString()+" ");
}
System.out.println();
}
10 、求两个集合的并集
public LinkedSet union(LinkedSet set){
LinkedSet setTemp = new LinkedSet(); //定义一个新的链表,用来存放两个集合的并集
//setTemp添加当前集合的元素
Node temp = head.next;
while(temp!=null){
setTemp.add(temp.element);
temp = temp.next;
}
//setTemp添加set集合的元素
Node h = set.head.next;
while(h!=null){
setTemp.add(h.element);
h = h.next;
}
return setTemp;
}
11 、求两个集合的交集
public LinkedSet intersection(LinkedSet set){
LinkedSet setTemp = new LinkedSet();
//setTemp添加当前集合的元素
Node temp = head.next;
while(temp!=null){ //遍历当前对象的所有结点
if(set.contains(temp.element)){ //判断set集合是否包含该元素
setTemp.add(temp.element); //如果包含该元素,则添加到setTemp中
}
temp = temp.next; //temp指向后继结点
}
return setTemp;
}
12 、清除集合中的所有元素
public void clear(){
length = 0; //将链表的长度设置为0
head.next = null; //将附加头结点的next置为null,如果不置为null,则该链表的元素还存在
}
总结
单向链表的具体操作还是比较简单的,通过与集合的顺序表相比,我们很明显的发现,链表的插入和删除比顺序表要方便的多,顺序表需要对数组元素进行移动,而链表只需要改变指针域即可。但是对于查找操作来说顺序表效率更高,通过索引即可查找,而链表则需要进行遍历,大大的降低了效率。