目录
一、前言
1.1线性表的基本概念
线性表是由n个数据元素所构成的有限序列.
对于同一个线性表,其每一个数据元素的值虽然不同,但必须具有相同的数据类型;同时,数据元素之间具有一种线性的或“一对一”的逻辑关系,即:
(1)第一个数据元素没有前驱,这个数据元素也称为开始结点。
(2)最后一个数据元素没有后继,这个数据元素也成为终端节点。
(3)除第一个和最后一个数据元素之外,其他元素有且仅有一个前驱和一个后继。
具有上述逻辑关系的数据结构也成为线性结构。线性表就是一种线性结构。
1.2线性表的抽象数据类型描述
线性表的结构简单,其长度可以动态地增长或收缩;可以对线性表中的任何数据元素进行访问和查找;数据元素的插入和删除操作可以在线性表中的任何位置上进行;求线性表中指定元素的前驱和后继;可以将两个线性表合并成一个线性表,或将一个线性表拆分成两个或多个线性子表等。下面说明几种主要的基本操作。
(1)clear():将一个已经存在的线性表置成空表。
(2)isEmpty():判断线性表是否为空,若为空,则返回true;否则,返回false。
(3)length():求线性表中数据元素个数并返回其值。
(4)get(i):读取并返回线性表中的第i个数据元素的值。其中i的取值范围为0<=i<=length()-1。
(5)insert(i,x):在线性表的第i个数据元素之前插入一个值为x的数据元素。其中i的取值范围为0<=i<=length()。当i=0时,在表头插入x;当i=length()时,在表尾插入x。
(6)remove(i):删除并返回线性表中第i个数据元素。其中的取值范围为0<=i<=length()-1。
(7)indexOf(x):返回线性表中首次出现指定数据元素的位序号,若线性表中不包含此元素,则返回-1.
(8)display():输出线性表中的各个数据元素的值。
线性表的抽象数据Java接口描述如下:
public interface IList {
public void clear();
public boolean isEmpty();
public int length();
public Object get(int i)throws Exception;
public void insert(int i,Object x)throws Exception;
public void remove(int i)throws Exception;
public int indexOf(Object x);
public void display();
}
二、线性表的顺序储存
2.1顺序表的特点
(1)在线性表中逻辑上相邻的数据元素,在物理储存位置上也是相邻的。
(2)储存密度高,但需要预先分配“足够应用”的储存空间,这可能将会造成储存空间的浪费。
(3)便于随机存取
(4)不便于插入和删除操作,这是因为在顺序表上进行插入和删除操作会引起大量数据元素的移动。
2.2单链表类的描述
由于单链表只需一个头指针就能唯一标识它,所以单链表类的成员变量只需设置一个头指针即可。
以下是单链表类的描述:
public class SqList implements IList {
private Object[] listElem;//线性表储存空间
private int curLen;//线性表的当前长度
public SqList(int maxSize){
curLen=0;//置顺序表的当前长度为0
listElem=new Object[maxSize];//为顺序表分配maxSize个储存单元
}
@Override
public void clear() {//置空
curLen=0;
}
@Override
public boolean isEmpty() {//判空
return curLen==0;
}
@Override
public int length() {//求线性表数据元素个数并返回其值
return curLen;
}
@Override
public Object get(int i)throws Exception{//读取
if(i<0||i>curLen-1)
throw new Exception("第"+i+"个元素不存在");
return listElem[i];
}
@Override
public void insert(int i, Object x)throws Exception {//插入
if(curLen==listElem.length)//判断顺序表是否已满
throw new Exception("顺序表已满");//抛出异常
if(i<0||i>curLen)
throw new Exception("插入位置不合法");//抛出异常
for(int j=curLen;j>i;j--)
listElem[j]=listElem[j-1];//插入位置及其之后的所有数据元素后移一位
listElem[i]=x;//插入x
curLen++;//表长加1
}
@Override
public void remove(int i)throws Exception {//删除
if(i<0||i>curLen-1)
throw new Exception("删除位置不合法");//抛出异常
for(int j=i;j<curLen-1;j++)
listElem[j]=listElem[j+1];//被删除元素之后的所有数据元素左移一个储存位置
curLen--;//表长减1
}
@Override
public int indexOf(Object x){ //返回线性表中首次出现指定的数据元素的位序号
int j=0;
while (j<curLen&&!listElem[j].equals(x))
j++;
if(j<curLen)//判断j的位置是否位于顺序表中
return j;//返回值为x的数据元素在顺序表中的位置
else
return -1;//值为x的数据元素在顺序表中不存在
}
@Override
public void display() {//输出
for(int j=0;j<curLen;j++)
System.out.print(listElem[j]+"");
System.out.println();
}
}
三、线性表的链式储存
3.1结点类的描述
单链表是由若干个结点连接而成的。因此,要实现单链表,首先需要设计节点类。节点类由两部分组成。其中,data是数据域,用来存放后继结点的地址。以下是节点类的描述:
public class Node {
public Object data;//存放结点值
public Node next;//后继结点的引用
//无参数是的构造函数
public Node(){
this(null,null);
}
//带一个参数时的构造函数
public Node(Object data){
this(data,null);
}
//带两个参数时的构造函数
public Node(Object data,Node next){
this.data=data;
this.next=next;
}
3.2单链表类的描述
由于单链表只需一个头指针就能唯一表示它,所以单链表类的成员变量只需设置一个头指针即可。以下是单链表类的描述:
public class LinkList implements IList {
public Node head;//单链表的头指针
public LinkList(){//单链表的构造函数
head=new Node();//初始化头结点
}
public LinkList(int n,boolean order) throws Exception{//构造一个长度为n的单链表
this();//初始化头结点
if(order)//用尾插法顺序建立单链表
create1(n);
else //用头插法逆位序建立单链表
create2(n);
}
//用头插法逆位序建立单链表,其中n为单链表的结点个数
public void create2(int n) throws Exception{
Scanner sc=new Scanner(System.in);//构造用于输入的对象
for(int j=0;j<n;j++)//逆序输入n个结点的数据域值
insert(0,sc.next());//生成新节点,插入到表头
}
//用尾插法顺序建立单链表,其中n为单链表的结点个数
public void create1(int n) throws Exception{
Scanner sc=new Scanner(System.in);
for(int j=0;j<n;j++)//输入n个结点的数据域值
insert(length(),sc.next());//生成新节点,插入到表尾
}
@Override
public void clear() {//置空
head.data=null;
head.next=null;
}
public boolean isEmpty() {//判空
return head.next==null;
}
public int length() {//求单链表的长度
Node p=head.next;
int length =0;
while(p!=null){
p=p.next;
++length;
}
return length;
}
public Object get(int i) throws Exception {//读取
Node p=head.next;//初始化,p指向首结点,j为计数器
int j=0;
while(p!=null&&j<i){//从首节点开始向后查找,直到p指向第i个结点或p为空
p=p.next;//指向后继结点
++j;//计数器的值增1
}
if(j>i||p==null){
throw new Exception("第"+i+"个元素不存在");//抛出异常
}
return p.data;//返回结点p的数据域值
}
//在带头结点的单链表中的第i个结点之前插入一个值为x的新节点
public void insert(int i, Object x) throws Exception {
Node p=head;
int j=-1;
while(p!=null&&j<i-1){//寻找第i个结点的前驱
p=p.next;
++j;
}
if(j>i-1||p==null)//不合法
throw new Exception("插入位置不合法");
Node s=new Node(x);//生成新节点
s.next=p.next;//修改链,使新节点插入单链表中
p.next=s;
}
public void remove(int i) throws Exception {//删除
Node p=head;
int j=-1;
while(p.next!=null&&j<i-1){//寻找第i个结点的前驱
p=p.next;
++j;
}
if(j>i-1||p.next==null){
throw new Exception("删除位置不合法");
}
p.next=p.next.next;//修改链指针,使待删除结点从单链表中脱离出来
}
public int indexOf(Object x) {//查找
Node p=head.next;
int j=0;
//下面从单链表中的首结点开始查找,直到p.data为x或到达单链表的表尾
while(p!=null&&p.data.equals(x)){
p=p.next;//指向下一个节点
++j;//计数器的值加1
}
if(p!=null)
return j;//返回值为x 的结点在单链表中的位置
else
return -1;//值为x的结点不在单链表中,则返回-1
}
public void display() {//输出
Node node = head.next;
while (node != null) {
System.out.print(node.data + " ");
node = node.next;
}
System.out.println();
}