Java实现单链表
本文将数据结构知识中重要知识点:数据元素,结点,数据类型,抽象数据类型,抽象数据类型的实现、以及对新定义的数据结构的应用等知识通过下述java代码的形式串联起来,以从宏观上对数据结构有一个透彻的理解和认识。
我们要使用单链表这个数据结构来解决问题的前提是首先得创建一个单链表数据结构。创建单链表数据结构,就得自定义个单链表的抽象数据类型,抽象数据类型只是对数据结构定义一组逻辑操作,没有具体的实现。在实际应用中,必须实现这个抽象数据类型,才能使用它们,而实现抽象数据类型依赖于数据存储结构。
1. 为单链表的数据元素自定义结点类 Node.java
/**
*为单链表自定义的结点类
*单链表结点类,泛型参数T指定结点的元素类型
*/
public class Node<T>
{
public T data; //数据域,保存数据元素
public Node<T> next; //地址域,引用后继结点
public Node(T data, Node<T> next)//构造结点,data指定数据元素,next指定后继结点
{
this.data = data;
this.next = next;
}
public Node()//无参构造函数
{
this(null, null);
}
//4、Node类可声明以下方法:
public String toString() //返回结点元素值对应的字符串
{
return this.data.toString();
}
public boolean equals(Object obj) //比较两个结点值是否相等,覆盖Object类的equals(obj)方法
{
return obj==this || obj instanceof Node && this.data.equals(((Node<T>)obj).data);
}
}
2.使用Java接口为线性表自定义的抽象数据类型 :LList.java
/**
* 为数据结构线性表自定义的抽象数据类型
* 在Java中,抽象数据类可以使用接口来描述
* 线性表接口LList,描泛型参数T表示数据元素的数据类型
*/
public interface LList<T> //线性表接口,泛型参数T表示线性表中的数据元素的数据类型
{
boolean isEmpty(); //判断线性表是否空
int length(); //返回线性表长度
T get(int i); //返回第i(i≥0)个元素
void set(int i, T x); //设置第i个元素值为x
void insert(int i, T x); //插入x作为第i个元素
void append(T x); //在线性表最后插入x元素
T remove(int i); //删除第i个元素并返回被删除对象
void removeAll(); //删除线性表所有元素
T search(T key); //查找,返回首次出现的关键字为key元素
String toString(); //返回显示线性表所有元素值对应的字符串
}
3.实现线性表接口---抽象数据类型的实现类-LinkedList.java(链表类,提供Llist接口中抽象方法的具体实现)
/**
*线性表的链式表示和实现
*带头结点的单链表类
*实现线性表接口
*/
public class LinkedList<T>implements LList<T>//带头结点的单链表类,实现线性表接口
{
protected Node<T> head;//头指针,指向单链表的头结点
//默认构造方法,构造空单链表。创建头结点,data和next值均为null
public LinkedList()
{
this.head=new Node<T>();
}
由指定数组中的多个对象构造单链表,采用尾插入构造单链表
public LinkedList(T[] element){
this(); //创建空单链表,只有头结点
Node<T> rear = this.head;//rear指向单链表的最后一个结点
/*
*若element==null,抛出空对象异常
*element.length==0时,构造空链表
*/
for(int i=0;i<element.length;i++){
rear.next=new Node<T>(element[i],null);//尾插入,创建结点链入rear结点之后
rear=rear.next;//rear指向新的链尾结点
}
}
//判断单链表是否为空,O(1)
public boolean isEmpty(){
return this.head.next==null;
}
//求链表的长度
public int length(){
int i=0;
Node<T> p = this.head.next;//p从单链表第一个结点开始
while(p!=null){ //若单链表未结束
i++;
p=p.next;//p到达后继结点
}
return i;
}
//返回单链表所有元素的描述字符串,形式为“(,)”,覆盖Object类的toString()方法,O(n)
public String toString(){
String str="(";
Node<T> p =this.head.next;
while(p!=null){
str+=p.data.toString();
if(p.next!=null){
str +=","; //不是最后一个结点时后加分隔符
}
p=p.next;
}
return str+=")";
}
//返回第i(i>=0)个元素,若i无效,则返回null
public T get(int i){
if(i>=0){
Node<T> p=this.head.next;
for(int j=0; p!=null&&j<i;j++)
p=p.next;
if(p!=null)
return p.data;//p指向第i个结点
}
return null;
}
//设置第i(i>=0)个元素值为x,若i指定序号无效则抛出序号越界异常
public void set(int i,T x){
if(x==null)
return;//不能设置空对象
if(i>=0){
Node<T> p =this.head.next;
for(int j =0;p!=null&&j<i;j++){
p=p.next;
}
if(p!=null)
p.data=x;
}else throw new IndexOutOfBoundsException(i+"");//抛出序号越界异常
}
//将x对象出插入在序号为i结点前,O(n)
public void insert(int i,T x){
if(x==null)
return;
Node<T> p =this.head;
for(int j =0;p.next!=null&&j<i;j++){
p=p.next;
}
p.next=new Node<T>(x,p.next);
}
//在单链表的最后添加对象,O(n)
public void append(T x){
insert(Integer.MAX_VALUE,x);
}
//删除序号为i的结点
public T remove(int i){
if(i>0){
Node<T> p=this.head;
for(int j =0;p.next!=null&&j<i;j++)
p=p.next;
if(p.next!=null){
T old=p.next.data;
p.next=p.next.next;
return old;
}
}
return null;
}
//删除链表所有元素,Java自动收回各结点所占用的内存空间
public void removeAll(){
this.head.next=null;
}
/*
public T search(T key){
if(key==null)
return null;
Node<T> p=this.head.next;
while(p!=null&&p.data.compareTo(key)<=0){
if(p.data.compareTo(key)==0)
return p.data;
p=p.next;
}
return null;
}
*/
//查找,返回首次出现的关键字为key的元素
public T search(T key)
{
if (key==null)
return null;
for (Node<T> p=this.head.next; p!=null; p=p.next)
if (p.data.equals(key))
return p.data;
return null;
}
}
4.利用前面新创建的单链表数据类型,实现单链表逆转 SinglyLinkedList_reverse.java
注:LinkedList<T>声明为泛型类,类型形式参数T表示链表元素的数据类型。当声明LinkedList类的对象并创建实例时,再指定泛型参数T的实际类型参数为一个确定的类,例如:LinkedList<String> list = new LinkedList<String>(); LinkedList<Integer> list = new LinkedList<Integer>(); 这样可保证一个链表中的所有数据元素是相同类及其子类的对象。如果向链表添加指定泛型以外的对象,则会出现编译错误。T 的实际类型参数必须是类,不能使int 、char等基本数据类型。如果需要表示基本数据类型,则必须使用对应数据类型的包装类,如Integer 、Character等。
/**
*利用前面新创建的单链表数据类型,实现单链表逆转
*/
public class SinglyLinkedList_reverse
{
//将单链表逆转,泛型方法,返回值类型前声明类型参数T
//public static <T> void reverse(SinglyLinkedList<T> list)
public static <T> void reverse(LinkedList<T> list)
{
Node<T> p=list.head.next, succ=null, front=null; //head必须声明为public
while (p!=null)
{
succ = p.next; //设置succ是p结点的后继结点
p.next = front; //使p.next指向p结点的前驱结点
front = p;
p = succ; //p向后走一步
}
list.head.next = front;
}
public static void main(String args[])
{
String value[]={"A","B","C","D","E","F"};
//SinglyLinkedList<String> list = new SinglyLinkedList<String>(value);
LinkedList<String> list = new LinkedList<String>(value);
System.out.println("list: "+list.toString());
reverse(list);
System.out.println("逆转后 "+list.toString());
}
}
/*
程序运行结果如下:
list: (A, B, C, D, E, F)
逆转后 (F, E, D, C, B, A)
*/