顺序表结构
顺序表就是按照顺序存储方式存储的线性表,该线性表的结点按照逻辑次序依次存放在计算机的一组连续的存储单元中,如图所示。
由于顺序表是依次存放的,只要知道了该顺序表的首地址及每个数据元素所占用的存储长度,那么就很容易计算出任何一个 数据元素(即数据结点)的位置。
一般的,假设顺序表中所有结点的类型相同,每个节点占用c个存储单元。其中第一个单元的存储地址是该节点的存储地址,并设顺序表中开始节点a1的存储地址(简称基地址)为LOC(a1),那么节点ai的存储地址LOC(ai)可以通过下视计算:
LOC(ai)=LOC(a1)+(i-1)c (1≤i≤n )
1准备数据
示例代码如下:
/**
* 定义顺序表的最大长度
*/
static final int MAXLEN = 100;
/**
* 定义节点
*/
class DATA {
//节点的关键字
String key;
String name;
int age;
}
/**
* 定义顺序表结构
*/
class SLType {
//保存顺序表的结构数组
DATA[] listData = new DATA[MAXLEN + 1];
//顺序表已存节点的数量
int ListLen;
}
在上述代码中,定义了顺序表的最大长度MALEN.顺序表数据元素的类DATA及顺序表的类SLType.在类SLType中,ListLen 为顺序表已存结点的数量,即当前顺序表的长度,ListData 是一个对象数组,用来存放各个数据结点。
其实,可以认为该顺序表是一个班级学生的记录。 其中,key 为学号,name为学生的名称,age为年龄。
由于Java语言中数组都是从下标0开始的。在这里,为了讲述和理解的方便,从下标1开始记录数据结点,下标 0的位置不使用。
2初始化顺序表
在使用顺序表之前先要创建一个空的顺序表,这里只需要设置顺序表的节点数量ListLen为0即可。后面需要添加的的数据元素将从顺序表的第一个位置存储,代码示例如下:
//初始化顺序表
void SLInit(SLType SL){
//初始化空表
SL.ListLen=0;
}
这里并没有清空一个顺序表,读者也可以采用相应的程序代码来清空,需要注意的是只需要将节点数量ListLen设置为0即可,这样如果顺序表中原来已有有数据,也会被覆盖,并不影响操作,反而提高了处理的速度。
3计算顺序表的长度
计算顺序表长度即计算线性表L中结点的个数。由于在类SLType中使用ListLen来表示顺序表的结点数量,因此程序只要返回该值就可以了。代码示例如下:
int SLLength(SLType SL) {
//返回顺序表的元素数量
return (SL.ListLen);
}
4插入节点
插入结点就是在线性表L的第i个位置插入一个新的结点,使得其后的结点编号依次加1。这时,插入一个新结点之后, 线性表L的长度将变为n+1.插入结点操作的难点在于随后的每个结点数据都要进行移动,计算量比较大。代码示例如下:
int SLInsert(SLType SL, int n, DATA data) {
int i;
if (SL.ListLen >= MAXLEN) {
System.out.println("顺序表已满,不能插入节点!");
return 0;
}
if (n < 1 || n > SL.ListLen-1) {
System.out.println("插入元素序号错误,不能插入元素!");
return 0;
}
//将顺序表中的数据向后移
for (i = SL.ListLen; i >= n; i--) {
SL.listData[i + 1] = SL.listData[i];
}
SL.listData[n] = data;
SL.ListLen++;
//成功插入返回1
return 1;
}
在上述代码中,在程序中首先判断顺序表结点数量是否已超过最大数量,以及插入结点序号是否正确。所有条件都满足后,然而将顺序表中的数据向后移动,同时插入结点,并更新结点数量ListLen.
5追加结点
追加结点井不是一个基本的数据结构运算,其可以看作插入结点的一种特殊形式, 相当于在顺序表的末尾新增一个数据结点。 由于追加结点的特殊性, 其代码实现相比插入结点要简单,因为不必进行大量数据的移动,因此这里单独给出其实现的程序。代码示例如下:
int SLAdd(SLType SL, DATA data) {
if (SL.ListLen >= MAXLEN) {
System.out.println("顺序表已满,不能再添加节点了!");
return 0;
}
SL.listData[++ListLen] = data;
return 1;
}
6删除结点
删除结点即删除线性表L中的第i个结点,使得其后的所有结点编号依次减1。删除一个结点之后,线性表L的长度将变为n- 1。删除结点和插入结点类似,都需要进行大量数据的移动。代码示例如下:
int SLDelete(SLType SL, int n) {
int i;
if (n < 1 || n > SL.ListLen + 1) {
System.out.println("删除元素序号错误,不能删除元素!");
return 0;
}
//将顺序表中的数据向前移
for (i = n; i < SL.ListLen; i++) {
SL.listData[i] = SL.listData[i + 1];
}
SL.ListLen--;
return 1;
}
7查找节点
查找结点即在线性表L中查找值为x的结点,并返回该结点在线性表L中的位置。如果在线性表中没有找到值为x的结点,则返回一个错误标志。 这里需要注意的是,线性表中有可能含有多个与x值相同的结点,那么此时就返回第一次查找到的结点。根据值x的类型不同,查找结点可以分为按照序号查找和按照关键字查找节点的两种方法。
1)按照序号查找
对于一个顺序表,序号就是数据元素再数组中的位置,也就是数组的下标标号。按照序号查找节点是顺序表查找节点最常用的方法,这是因为顺序表的存储本身就是个数组。 代码示例如下:
//根据序号返回数据元素
DATA SLFindByNum(SLType SL, int n) {
if (n < 1 || n > SL.ListLen + 1) {
System.out.println("节点序号错误,不能返回节点!");
return null;
}
return SL.listData[n];
}
2)按照关键字查找结点
这里关键字可以是数据元素结构中的任意项。以key为关键字进行介绍,由前面知道key可以看作学生的学号。 代码示例如下:
//根据关键字查询节点
int SLFindByCont(SLType SL, String key) {
for (int i = 1; i <= SL.ListLen; i++) {
if (SL.listData[i].key.compareTo(key) == 0) {
return i;
}
}
return 0;
}
8显示所有的节点
int SLAll(SLType SL) {
for (int i = 1; i <= SL.ListLen; i++) {
System.out.println(SL.listData[i].key + " " + SL.listData[i].name + " " + SL.listData[i].age);
}
return 0;
}
9操作实例
新建一个名叫SequentialList的类
import java.util.Scanner;
/**
* 定义节点
*/
class DATA {
//节点的关键字
String key;
String name;
int age;
}
/**
* 定义顺序表结构
*/
class SLType {
// 定义顺序表的最大长度
static final int MAXLEN = 100;
//保存顺序表的结构数组
DATA[] listData = new DATA[MAXLEN + 1];
//顺序表已存节点的数量
int ListLen;
//初始化顺序表
void SLInit(SLType SL) {
//初始化空表
SL.ListLen = 0;
}
int SLLength(SLType SL) {
//返回顺序表的元素数量
return (SL.ListLen);
}
int SLInsert(SLType SL, int n, DATA data) {
int i;
if (SL.ListLen >= MAXLEN) {
System.out.println("顺序表已满,不能插入节点!");
return 0;
}
if (n < 1 || n > SL.ListLen - 1) {
System.out.println("插入元素序号错误,不能插入元素!");
return 0;
}
//将顺序表中的数据向后移
for (i = SL.ListLen; i >= n; i--) {
SL.listData[i + 1] = SL.listData[i];
}
SL.listData[n] = data;
SL.ListLen++;
//成功插入返回1
return 1;
}
int SLAdd(SLType SL, DATA data) {
if (SL.ListLen >= MAXLEN) {
System.out.println("顺序表已满,不能再添加节点了!");
return 0;
}
SL.listData[++ListLen] = data;
return 1;
}
int SLDelete(SLType SL, int n) {
int i;
if (n < 1 || n > SL.ListLen + 1) {
System.out.println("删除元素序号错误,不能删除元素!");
return 0;
}
//将顺序表中的数据向前移
for (i = n; i < SL.ListLen; i++) {
SL.listData[i] = SL.listData[i + 1];
}
SL.ListLen--;
return 1;
}
//根据序号返回数据元素
DATA SLFindByNum(SLType SL, int n) {
if (n < 1 || n > SL.ListLen + 1) {
System.out.println("节点序号错误,不能返回节点!");
return null;
}
return SL.listData[n];
}
//根据关键字查询节点
int SLFindByCont(SLType SL, String key) {
for (int i = 1; i <= SL.ListLen; i++) {
if (SL.listData[i].key.compareTo(key) == 0) {
return i;
}
}
return 0;
}
int SLAll(SLType SL) {
for (int i = 1; i <= SL.ListLen; i++) {
System.out.println(SL.listData[i].key + " " + SL.listData[i].name + " " + SL.listData[i].age);
}
return 0;
}
}
public class SequentialList {
public static void main(String[] args) {
int i;
//定义顺序表变量
SLType SL = new SLType();
//定义节点保存引用变量
DATA pdata;
//保存关键字
String key;
System.out.println("顺序表操作演示!");
//初始化顺序表
SL.SLInit(SL);
System.out.println("初始化顺序表完成!");
Scanner input = new Scanner(System.in);
do {
System.out.println("输入添加的节点(学号 姓名 年龄):");
DATA data = new DATA();
data.key = input.next();
data.name = input.next();
data.age = input.nextInt();
if (data.age != 0) {
//若添加节点失败
if (SL.SLAdd(SL, data) == 0) {
break;
}
} else {
break;
}
} while (true);
System.out.println("顺序表中的节点顺序为:");
SL.SLAll(SL);
System.out.println("要取出节点的序号:");
i = input.nextInt();
pdata = SL.SLFindByNum(SL, i);
if (pdata != null) {
System.out.println("第" + i + "个节点为:" + pdata.key + pdata.name + pdata.age);
}
System.out.println("要查找节点的关键字:");
key = input.next();
i = SL.SLFindByCont(SL, key);
pdata = SL.SLFindByNum(SL, i);
if (pdata != null) {
System.out.println("第" + i + "个节点为:" + pdata.key + pdata.name + pdata.age);
}
}
}
当age输入等于0时退出循环
结果如下: