(第一次写,会有很多废话吧,敬请喷我,帮我进步,哈哈哈哈哈)
(刚刚完成了顺序表示和实现,记录一下吧,后续会更新…)
线性表的顺序表示和实现
一、基础知识
1、线性表
- 一个线性表是n个数据元素的有限序列,数据元素的具体含义视情况而定,可以是一个数、一个符号或者一页书,甚至可以更复杂。
例如:(a,b,c,d,e,f,g)表中的每个字母都是一个元素,它们组成了一个简单的线性表。 - 在稍复杂的线性表中,一个数据元素可以由若干个数据项组成。在这种情况下,常把数据元素称为记录,含有大量记录的线性表又称文件。
例如:班级学生信息登记表,每个学生的信息为一个记录,它由姓名、性别、年龄等数据项组成。 - 线性表中元素的个数n(n ≥ 0)定义为线性表的长度,n = 0时称为空表。
2、线性表的顺序表示
-
线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素。
例如:LOC(ai) = LOC(a1) + L
式中,LOC(ai)表示线性表中第i个元素的存储位置,L表示每个元素需占用L个存储单元,LOC(a1)是线性表中第一个数据元素的存储位置且被称为线性表的起始位置或基地址。
-
线性表的这种机内表示称做线性表的顺序存储结构或顺序映像,通常,称这种存储结构的线性表为顺序表。它的特点是,为表中相邻的元素
ai 和 a(i+1) 赋以相邻的存储位置 LOC(ai) 和 LOC[a(i+1)]
。换句话说:以元素在计算机内“物理位置相邻“来表示线性表中数据元素的逻辑关系。 -
线性表的顺序存储结构是一种随机存取的存储结构。
注: 个人理解的随机存取就是可以直接存取线性表中任意一个元素。
二、实现内容
1、线性表元素的顺序表示和实现
- 构造一个线性表
构造线性表使用了结构体,结构体中包含了一个整型指针变量 *elem (用来访问线性表中的元素)、两个整型变量length(线性表当前存储元素所占长度)、listsize(线性表当前最大长度)。
typedef struct SqList{ //构造线性表的动态分配顺序存储结构
ElemType *elem;
ElemType length;
ElemType listsize;
}SqList;
- 删除线性表
删除线性表时,首先使用 free() 函数将基地址释放,然后将线性表长度置 0 。
注:我在末尾设置了一个输出用来表示删除成功。
ElemType DestroyList(SqList &L) //删除线性表
{
if (L.elem)
free(L.elem); //释放基址
L.length = 0; //线性表长度置0
printf("删除线性表成功!\n");
return 0;
}//DestroyList_sq
- 将线性表置空
首先,我选择使用 ListEmpty(SqList L) 检查线性表是否为空,若为空表直接返回。
然后, 我选择了将线性表的长度直接置 0 来清空线性表。
注: 我不清楚是否有其他选择,如果有的话欢迎指教。
ElemType ClearList(SqList L) //清空线性表元素
{
if (ListEmpty(L))
return 0;
L.length = 0;
return 0;
}
- 检查线性表是否为空
检查线性表的长度是否为 0 来判断是否为空。
bool ListEmpty(SqList L) //检查线性表是否为空
{
if (L.length)
return false; //如果不为空则返回false
return true;
}//ListEmpty_sq
- 返回线性表的长度
直接返回线性表的长度属性
ElemType ListLength(SqList L)
{
return L.length;
}
- 返回线性表中某一个元素
ElemType GetElem(SqList L, ElemType i, ElemType &e) //用e返回第i个位置的值
{
if (i < 1 || i > L.length) //检查i的位置是否合法
return -1;
e = L.elem[i - 1];
return 0;
}//GetElem_sq
- 返回给定元素在线性表中的位置
将线性表中的元素逐个遍历,并与给定元素对比,如相同,便返回其位置;反之,则返回错误。
ElemType LocateElem(SqList L, ElemType e) //查找e元素在线性表中的位置
{
for (ElemType i = 0; i < L.length; i++)
if (e == L.elem[i])
return i + 1;
return -1;
}//Locate_sq
- 线性表元素的插入
首先,检查 i 值是否合法,即是否在线性表中或末尾后一位。
然后,如果 i 值合法,检查存储空间是否已满,如果满了,便执行增加容量过程。
最后,将元素插入对应位置,并移动相应元素。
ElemType ListInsert(SqList &L , ElemType i , ElemType e) //在顺序线性表L中第i个位置之前插入新的元素,i的合法值为1≤i≤ListLength+1
{
if (i<1 || i > L.length + 1) //i值不合法
return -1;
if (L.length >= L.listsize) //当前存储空间已满
{
ElemType *newbase = (ElemType *)realloc(L.elem, (L.listsize + LISTINCREMENT) * sizeof(ElemType)); //新基址分配
if (!newbase) //存储分配失败
return -1;
L.listsize += LISTINCREMENT; //增加存储容量
printf("存储容量增加成功!\n");
}
ElemType *q , *p; //q为插入位置,r为临时变量
q = &(L.elem[i - 1]);
for (p = &(L.elem[L.length - 1]); p >= q; --p) //插入位置后面的元素右移一位
*(p + 1) = *p;
*q = e; //插入e
++L.length; //表长+1
return 0;
}//ListInsert_sq
- 线性表中指定元素的删除
首先,检查 i 是否存在于线性表中;
然后,如果存在,便执行删除过程,并移动相应元素。
ElemType ListDelete(SqList &L, ElemType i, ElemType &e) //删除第i个位置的元素,并返回其值
{
if (i < 1 || i > L.length) //检查i值是否合法
return -1;
ElemType *q , *p; //p指向第i个元素,q指向线性表末尾元素
p = &(L.elem[i - 1]);
e = *p;
q = L.elem + L.length - 1;
for (p; p < q; p++) //第i个位置的元素均向左移
*p = *(p + 1);
L.length -= 1; //线性表长度减1
printf("删除成功\n");
return 0;
}
- 线性表中所有元素的遍历
ElemType ListVisit(SqList L) //线性表元素遍历
{
if (!L.length)
return -1;
for (ElemType i = 0; i < L.length; i++)
printf("%d ", L.elem[i]);
return 0;
}
三、代码展示
#include<stdio.h>
#include<stdlib.h>
#define LIST_INIT_SIZE 100 //线性表的初始容量为100
#define LISTINCREMENT 10 //线性表存储空间的分配增量
typedef int ElemType;
typedef struct SqList{ //构造线性表的动态分配顺序存储结构
ElemType *elem;
ElemType length;
ElemType listsize;
}SqList;
ElemType InitList(SqList &L) //构造一个空的线性表L
{
L.elem = (ElemType *)malloc(LIST_INIT_SIZE * sizeof(ElemType));
if (!L.elem)
return -1;
L.length = 0;
L.listsize = LIST_INIT_SIZE;
return 0;
}//InitList_sq
ElemType ClearList(SqList L) //清空线性表元素
{
if (ListEmpty(L))
return 0;
L.length = 0;
return 0;
}
ElemType DestroyList(SqList &L) //删除线性表
{
if (L.elem)
free(L.elem); //释放基址
L.length = 0; //线性表长度置0
printf("删除线性表成功!\n");
return 0;
}//DestroyList_sq
ElemType ListInsert(SqList &L , ElemType i , ElemType e) //在顺序线性表L中第i个位置之前插入新的元素,i的合法值为1≤i≤ListLength+1
{
if (i<1 || i > L.length + 1) //i值不合法
return -1;
if (L.length >= L.listsize) //当前存储空间已满
{
ElemType *newbase = (ElemType *)realloc(L.elem, (L.listsize + LISTINCREMENT) * sizeof(ElemType)); //新基址分配
if (!newbase) //存储分配失败
return -1;
L.listsize += LISTINCREMENT; //增加存储容量
printf("存储容量增加成功!\n");
}
ElemType *q , *p; //q为插入位置,r为临时变量
q = &(L.elem[i - 1]);
for (p = &(L.elem[L.length - 1]); p >= q; --p) //插入位置后面的元素右移一位
*(p + 1) = *p;
*q = e; //插入e
++L.length; //表长+1
return 0;
}//ListInsert_sq
int ListDelete(SqList &L, ElemType i, ElemType &e) //删除第i个位置的元素,并返回其值
{
if (i < 1 || i > L.length) //检查i值是否合法
return -1;
ElemType *q , *p; //p指向第i个元素,q指向线性表末尾元素
p = &(L.elem[i - 1]);
e = *p;
q = L.elem + L.length - 1;
for (p; p < q; p++) //第i个位置的元素均向左移
*p = *(p + 1);
L.length -= 1; //线性表长度减1
printf("删除成功\n");
return 0;
}
bool ListEmpty(SqList L) //检查线性表是否为空
{
if (L.length)
return false; //如果不为空则返回false
return true;
}//ListEmpty_sq
ElemType GetElem(SqList L, ElemType i, ElemType &e) //用e返回第i个位置的值
{
if (i < 1 || i > L.length) //检查i的位置是否合法
return -1;
e = L.elem[i - 1];
return 0;
}//GetElem_sq
ElemType LocateElem(SqList L, ElemType e) //查找e元素在线性表中的位置
{
for (ElemType i = 0; i < L.length; i++)
if (e == L.elem[i])
return i + 1;
return -1;
}//Locate_sq
ElemType ListVisit(SqList L) //线性表元素遍历
{
if (!L.length)
return -1;
for (int i = 0; i < L.length; i++)
printf("%d ", L.elem[i]);
return 0;
}
int main()
{
SqList L; //定义线性表存储结构体变量
InitList(L); //初始化线性表
ElemType e;
for (ElemType i = 1; i < 10; i++)
ListInsert(L, i, i); //依次向线性表中插入10个元素
ListVisit(L); //遍历线性表中元素
ListDelete(L, 5, e); //删除线性表中第5个元素
printf("%d\n", e);
for (ElemType i = 0; i < L.length; i++) //使用循环逐个遍历线性表中元素
printf("%d\n", L.elem[i]);
printf("%d\n", LocateElem(L, 6)); //查找元素6在线性表中的位置
GetElem(L, 8, e); //返回线性表中第8个元素
printf("%d\n", e);
DestroyList(L); //删除线性表
if (ListEmpty(L))
printf("此线性表为空!\n");
return 0;
}
四、总结
- 线性表顺序存储结构的优点在于表中元素值的访问和修改比较方便,但是由于插入和删除需要移动相应的元素,在频繁进行修改大量元素时会非常耗时间。
- 在实现过程中使用的 malloc函数 和 realloc函数 均使用的是 stdlib.h头文件 。
- 起初只是看书感觉自己过程都了解了,在实现上还是磕磕绊绊,认识到了”纸上谈兵“的错误性。
- 刚刚开始,还是个小*2^1024的小白菜,如果建议或错误敬请提出,我一定诚心接受并改进。
- 非常希望有同样爱好的 初学者或者老鸟一起玩耍,哈哈哈哈!