前言
线性结构的基本特点是除了第一个元素无直接前驱,最后一个元素无直接后续之外,其他每个数据元素都有一个前驱和后继. 线性表是最基本且最常用的一种线性结构,同时它也是其他的数据结构的基础. 尤其是单链表是贯穿于整个数据结构课程的基本技术点.
一. 线性表的定义和特点
在我们生活中有那些线性表的例子了?
例如,26个字母表; 例如学生基本信息表.每个学生为一个数据元素,包含学号,姓名,专业等数据项目.
满足数据元素不同,但是在同一个线性表中的元素必定具有相同的特点,即属于同一数据对象, 相邻数据元素之间存在这个序偶关系. 诸如此类由(n>=0)个数据特性相同的元素构成的有限序列称为"线性表".
线性表中的元素的个数n定义为线性表的长度,如果n = 0则称为空表.
对于非空的线性表和线性结构,其特点如下:
- 存在唯一的一个被称作"第一个"的数据元素
- 存在唯一的一个呗称作"最后一个"的数据元素
- 除了第一个之外,结构中的每个数据元素均有一个前驱
- 除了最后一个之外,结构中的每个数据元素都有一个后继.
1.2 线性表的类型定义
线性表是一个相当灵活的数据结构,其长度可根据需要增长或缩短,即对线性表的数据元素不仅可以进行访问,而且可以对其进行插入和删除等操作.
ADT List{
Data: 线性表的数据对象集合为{a1,a2,......an},每个元素的类型均为DataType. 其中,除了第一个元素a1外,每一个元素有且只有一个直接前驱元素,除了最后一个元素an外,每个元素有且只有一个直接后继元素. 数据元素之间的关系是一对一的关系.
Operation
InitList(&L)
操作结果:初始化操作,建立一个空的线性表L.
DestroyList(&L)
初始条件: 线性表L已存在
操作结果: 销毁线性表L.
ClearList(&L)
初始条件: 线性表L已存在
操作结果: 将L重置为空表.
ListEmpty(L)
初始条件: 线性表L已存在
操作结果: 若L为空表,则返回true,否则返回false.
ListLength(L)
初始条件: 线性表L已存在
操作结果: 返回L中数据元素的个数
GetElem(L,i,&e)
初始条件: 线性表L已存在,且1<=i<ListLength(L)
操作结果: 用e返回L中第i个数据元素的值;
LocateElem(L,e)
初始条件: 线性表L已存在
操作结果: 返回L中第1个值与e相同的元素在L中的位置. 若数据不存在则返回0;
PriorElem(L,cur_e,&pre_e);
初始条件: 线性表L已存在
操作结果: 若cur_e是L的数据元素,且不是第一个,则用pre_e返回其前驱,否则操作失败.
NextElem(L,cur_e,&next_e);
初始条件: 线性表L已存在
操作结果: 若cur_e是L的数据元素,且不是最后一个,则用next_e返回其后继,否则操作失败.
ListInsert(L,i,e);
初始条件: 线性表L已存在,且1<=i<=listLength(L)
操作结果: 在L中第i个位置之前插入新的数据元素e,L长度加1.
ListDelete(L,i);
初始条件: 线性表L已存在,且1<=i<=listLength(L)
操作结果: 删除L的第i个元素,L的长度减1.
TraverseList(L);
初始条件: 线性表L已存在
操作结果: 对线性表L进行遍历,在遍历的过程中对L的每个结点访问1次.
}ADT List.
1.3 线性表的顺序表示与实现
1.3.1 线性表的顺序存储表示
线性表的顺序表示指的是一组地址连续的存储单元依次存储线性表的数据元素,这种表示也称为线性表的顺序存储结构或顺序映像. 通常,称这种存储结构的线性表为顺序表(Sequential List). 其特点是, 逻辑上相邻的数据元素,其物理次序也是相邻的.
顺序表的存储结构
#define MAXSIZE 100
#define ElemType int
typedef struct {
ElemType *data;
int length;
}Sqlist;
1.3.2 顺序表的基本操作的实现
算法1.1 顺序表的初始化
顺序表的初始化就是构成一个空的顺序表; 算法步骤:
- 为顺序表L动态分配一个预定义大小的数组空间,使elem 指向这段空间的基地址;
- 将表的当前长度设置为0;
算法实现
//初始化
Status InitList(Sqlist *L){
//为顺序表分配一个大小为MAXSIZE 的数组空间
L->data = malloc(sizeof(ElemType) * MAXSIZE);
//存储分配失败退出
if(!L->data) exit(ERROR);
//空表长度为0
L->length = 0;
return OK;
}
main函数调用
int main(int argc, const char * argv[]) {
// insert code here...
printf("Hello, Data Structure!\n");
Sqlist L;
Sqlist Lb;
ElemType e;
Status iStatus;
int j,k;
iStatus = InitList(&L);
printf("初始化L后: L.Length = %d\n", L.length);
return 0;
}
结果
Hello, Data Structure!
初始化L后: L.Length = 0
Program ended with exit code: 0
算法1.2 顺序表插入
算法步骤:
- 判断插入位置i是否合法(i值的合法范围1<=i<n+1),若不合法则返回ERROR;
- 判断顺序表的存储空间是否已满,若满则返回ERROR;
- 将第n个至第i个位置的元素依次向后移动一个位置,空出第i个位置(i = n+1)时无需要移动;
- 将要插入的新元素e放入第i个位置;
- 表长累积1.
算法实现:
//1.2 顺序表的插入
/*
初始条件:顺序线性表L已存在,1≤i≤ListLength(L);
操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1
*/
Status ListInsert(Sqlist *L,int i,ElemType e){
//i值不合法判断
if((i<1) || (i>L->length+1)) return ERROR;
//存储空间已满
if(L->length == MAXSIZE) return ERROR;
//插入数据不在表尾,则先移动出空余位置
if(i <= L->length){
for(int j = L->length-1; j>=i-1;j--){
//插入位置以及之后的位置后移动1位
L->data[j+1] = L->data[j];
}
}
//将新元素e 放入第i个位置上
L->data[i-1] = e;
//长度+1;
++L->length;
return OK;
}
main函数调用
int main(int argc, const char * argv[]) {
...
//1.2 顺序表数据插入
for(int j=1; j <= 5;j++){
iStatus = ListInsert(&L, 1, j);
}
printf("插入数据L长度: %d\n",L.length);
}
结果
Hello, Data Structure!
初始化L后: L.Length = 0
插入数据L长度: 5
Program ended with exit code: 0
算法1.3 顺序表取值
算法步骤
- 判断指定的位置序号i是否合理(1<=i<L.length),若不合理,则返回ERROR;
- 若i值合理,则将第i个数据元素L.data[i-1]赋值给参数e, 通过e返回第i个数据元素的传值.
//1.2 顺序表的取值
Status GetElem(Sqlist L,int i, ElemType *e){
//判断i值是否合理, 若不合理,返回ERROR
if(i<1 || i > L.length) return ERROR;
//data[i-1]单元存储第i个数据元素.
*e = L.data[i-1];
return OK;
}
main函数调用
int main(int argc, const char * argv[]) {
...
//1.3 顺序表取值
GetElem(L, 5, &e);
printf("顺序表L第5个元素的值为:%d\n",e);
}
结果
Hello, Data Structure!
初始化L后: L.Length = 0
插入数据L长度: 5
顺序表L第5个元素的值为:1
Program ended with exit code: 0
算法1.4 顺序表删除
算法步骤
- 判断删除位置i的合法(合法值 1 <= i <=n),若不合法则返回ERROR;
- 判断线性表是否为空
- 将第 i+1 个至第 n 个的元素依次向前移动一个位置( i=n 时无需移动)
- 表长减1;
算法实现:
//1.4 顺序表删除指定元素
/*
初始条件:顺序线性表L已存在,1≤i≤ListLength(L)
操作结果: 删除L的第i个数据元素,L的长度减1
*/
Status ListDelete(Sqlist *L,int i){
//线性表为空
if(L->length == 0) return ERROR;
//i值不合法判断
if((i<1) || (i>L->length+1)) return ERROR;
for(int j = i; j < L->length -1 ;j++){
//被删除元素之后的元素向前移动
L->data[j-1] = L->data[j];
}
//表长度-1;
L->length --;
return OK;
}
main函数调用
int main(int argc, const char * argv[]) {
...
//1.4 顺序表删除第2个元素
ListDelete(&L, 2);
printf("顺序表删除第%d元素,长度为%d\n",2,L.length);
}
结果
Hello, Data Structure!
初始化L后: L.Length = 0
插入数据L长度: 5
顺序表L第5个元素的值为:1
顺序表删除第2元素,长度为4
Program ended with exit code: 0
二.继续探索
大家可以根据文章前面的学习,完成以下的需求!
2.1 问题
ClearList(&L)
初始条件: 线性表L已存在
操作结果: 将L重置为空表.
ListEmpty(L)
初始条件: 线性表L已存在
操作结果: 若L为空表,则返回true,否则返回false.
ListLength(L)
初始条件: 线性表L已存在
操作结果: 返回L中数据元素的个数
TraverseList(L);
初始条件: 线性表L已存在
操作结果: 对线性表L进行遍历,在遍历的过程中对L的每个结点访问1次.
LocateElem(L,e)
初始条件: 线性表L已存在
操作结果: 返回L中第1个值与e相同的元素在L中的位置. 若数据不存在则返回0;
PriorElem(L,cur_e,&pre_e);
初始条件: 线性表L已存在
操作结果: 若cur_e是L的数据元素,且不是第一个,则用pre_e返回其前驱,否则操作失败.
NextElem(L,cur_e,&next_e);
初始条件: 线性表L已存在
操作结果: 若cur_e是L的数据元素,且不是最后一个,则用next_e返回其后继,否则操作失败.
2.2 参考答案
//1.5 清空顺序表
/* 初始条件:顺序线性表L已存在。操作结果:将L重置为空表 */
Status ClearList(Sqlist *L)
{
L->length=0;
return OK;
}
//1.6 判断顺序表清空
/* 初始条件:顺序线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE */
Status ListEmpty(Sqlist L)
{
if(L.length==0)
return TRUE;
else
return FALSE;
}
//1.7 获取顺序表长度
/* 初始条件:顺序线性表L已存在。操作结果:返回L中数据元素个数 */
int ListLength(Sqlist L)
{
return L.length;
}
//1.8 顺序输出List
/* 初始条件:顺序线性表L已存在 */
/* 操作结果:依次对L的每个数据元素输出 */
Status TraverseList(Sqlist L)
{
int i;
for(i=0;i<L.length;i++)
printf("%d\n",L.data[i]);
printf("\n");
return OK;
}
//1.9 顺序表查找元素并返回位置
/* 初始条件:顺序线性表L已存在 */
/* 操作结果:返回L中第1个与e满足关系的数据元素的位序。 */
/* 若这样的数据元素不存在,则返回值为0 */
int LocateElem(Sqlist L,ElemType e)
{
int i;
if (L.length==0) return 0;
for(i=0;i<L.length;i++)
{
if (L.data[i]==e)
break;
}
if(i>=L.length) return 0;
return i+1;
}