线性表
文章目录
基础
线性表:由零个或者多个数据元素组成的有限序列。
- 线性表是一个序列,多个元素先来后到
- 若元素存在多个,则第一个元素无前驱,最后一个元素无后继,其他元素有且只有一个前驱和后继
- 线性表是有限的
🎨如果用数学语言来进行定义,例如将线性表的元素表示为:
(a1,a2.......a(i-1), ai, a(i+1),.......an
其中ai-1领先于ai,ai领先于a(i+1),称a(i-1)为ai的直接前驱元素,a(i+1)是ai的直接后继元素。
线性表的元素个数n(n>=0)
定义为线性表的长度,当长度n=0
时,称为空表。
例如下图表示了典型的线性表:
🎨线性表的抽象数据类型:
ADT 线性表(List)
数据对象:D={
ai | ai ∈ ElemSet, i = 1, 2, 3..., n, n >= 0}
数据关系:R={
<a(i-1), ai> | a(i-1), ai ∈ D, i = 2, .... n }
基本操作:
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返回其前驱,否则操作失败,pre_e无定义
NextElem(L, cur_e, &next_e)
初始条件:线性表L已存在
操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回其后继,否则操作失败,next_e无定义
ListInsert(&L, i, e)
初始条件:线性表L已存在
操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1
ListDelete(&L, i, *e)
初始条件:线性表L已存在
操作结果:删除L的第i个数据元素,L的长度减1
TraverseList(L)
初始条件:线性表L已存在
操作结果:对线性表L进行遍历,在遍历过程中对L的每个结点访问一次
end ADT
线性表的物理结构:
顺序存储结构
顺序存储结构的封装需要三个属性:
- 存储空间的起始位置,数组
data
, 它的存储位置就是线性表存储空间的起始位置 - 线性表大最大存储容量:数组的容量
MAX_SIZE
- 线性表的当前长度:
length
🏁地址计算方法:
假设ElemType占用的是c个存储单位(字节)
那么顺序表中第i+1个数据元素和第i个数据元素的存储位置的关系是(LOC表示获得存储位置的函数):
LOC(a( i + 1 ) ) = LOC(a ( i ) ) + c
而顺序表中第i个元素的存储位置可以由a1的位置推算出:
LOC(a ( i ) ) = LOC( a1 ) + ( i -1 ) * c
🏀C语言实现
初始化
我们以学生表为例,其具体数据结构如下:
名称 | 成员变量 |
---|---|
学号 | no |
姓名 | name |
年龄 | age |
然后我们通过结构体建立学生类型作为ElemType
,名为Student
#define MAXSIZE 10
typedef struct {
char no[10]; //编号
char name[50]; //姓名
int age; //年龄
} Student;
然后在此基础上实现,一个顺序表类型SqList
,存储一个学生类型的数组和顺序表长度:
typedef struct {
Student data[MAXSIZE]; //顺序表的数据
int length; //顺序表的长度
} SqList;
通过以下代码初始化这个顺序表(放置在主函数main()
中:
初始化5个学生的信息和一个空的顺序表,然后给顺序表依次赋值并更新长度:
Student stu1 = {
"2020001", "Yuri", 22};
Student stu2 = {
"2020002", "Alice", 22};
Student stu3 = {
"2020003", "LiLi", 17};
Student stu4 = {
"2020004", "Steve", 3};
Student stu5 = {
"2020005", "Bill", 65};
Student stu6 = {
"2020007", "Kiss", 65};
Student stu7 = {
"2020010", "Tim", 58};
SqList sqList;
sqList.length = 5;
sqList.data[0] = stu1;
sqList.data[1] = stu2;
sqList.data[2] = stu3;
sqList.data[3] = stu4;
sqList.data[4] = stu5;
显示
打印输出元素和顺序表的信息也是很重要的一个功能,我们通过遍历和取址完成:
/**
* 打印顺序表
* @param L 顺序表
*/
void PrintList(SqList L) {
printf("*********\n");
for (int i = 0; i < L.length; ++i) {
printf("编号:%d 学号:%s 姓名:%s 年龄:%d\n", i + 1, L.data[i].no, L.data[i].name, L.data[i].age);
}
printf("*********\n");
}
/**
* 打印元素
* @param e
*/
void PrintElem(Student e){
printf("学号:%s 姓名:%s 年龄:%d\n", e.no, e.name, e.age);
}
初始化完成后的顺序表数据如下:
编号:1 学号:2020001 姓名:Yuri 年龄:22
编号:2 学号:2020002 姓名:Alice 年龄:22
编号:3 学号:2020003 姓名:LiLi 年龄:17
编号:4 学号:2020004 姓名:Steve 年龄:3
编号:5 学号:2020005 姓名:Bill 年龄:65
获取、定位元素
实现GetElem的具体操作,只需要把线性表L第 i 个下标的值返回即可:
/**
* 取值
* @param L 顺序表的拷贝
* @param i 要查找元素的下标
* @param e 新的学生指针
* @return
*/
int GetElem(SqList L, int i, Student *e) {
int len = L.length;
if (i >= len || i < 0) {
return -1; //下标不合理
}
*e = L.data[i]; //将i处学生的地址赋给指针
}
使用这个方法
GetElem(sqList, 3, &newStu)