数据结构之顺序表
1.顺序表的结构
顺序表 是指用一组地址连续(存储空间紧邻)的存储单元依次存储线性表的数据元素。顺序表的 逻辑顺序 与 物理地址 是一致的,逻辑关系相邻的两个数据元素,在物理位置上也相邻。因此,线性表中各个数据元素之间的逻辑关系可以根据它们在存储空间的顺序来确定。
一维数组在内存中占用的存储空间就是用一组连续的存储区域,因此顺序表可以用一维数组表示。
由于线性表有插入、删除等运算,其长度是可变的,所以通常用一个指向连续存储区域的 指针(空间大小可以动态分配)代替一维数组,并将其和顺序表的长度封装成一个结构体进行描述。
顺序表的结构类型描述如下:
#define Max_Length 100 // 顺序表的最大长度
#define Increment_Length 10 // 顺序表存储空间分配增量
typedef int ElemType; // ElemType为顺序表的元素类型
typedef struct{
ElemType* data; // data指向存储数据元素的一堆数组,初始大小为 Max_Length
int Length; // 顺序表的实际长度,其值小于等于 ListLength
int ListLength; // 当前顺序表分配的空间大小,初始值为 Max_Length
}SeqList;
2.顺序表基本操作的实现
通常情况下,需要了解一些操作的执行状态(例如成功、失败等),为此引入一个抽象数据类型 Status
,用其返回操作的执行状态。例如:
#define OK 0 // 成功执行
#define Err_Memory -1 // 内存非配错误
#define Err_InvalidParam -2 // 输入参数无效
#define Err_Overflow -3 // 溢出错误
#define Err_IllegalPos -4 // 非法位置
#define Err_NoResult -5 // 无返回结果或返回结果为空
上述常量定义语句可以放在顺序表结构定义语句之前,同时用如下语句将 Status
实例化为整型
typedef int Status;
1.初始化顺序表
初始化顺序表时,首先要为顺序表分配一个预定义大小(由 Max_Length
确定)的一维数组空间,然后将线性表的实际长度和分配空间大小分别置为 0 和 Max_Length
。算法描述如下:
Status InitList(SeqList *L){
L->data = (ElemType*)malloc(Max_Length * sizeof(ElemType));
if(!L->data)
return Err_Memory;
L->Length = 0; // 顺序表的实际长度置为0
L->ListLength = Max_Length; // 顺序表的存储大小置为 Max_Length
return OK;
}
2.清空线性表
顺序表中实现清空操作非常简单,不需要改变当前的分配空间,只需将顺序表的实际长度置为 0 即可。
Status ClearList(SeqList *L){
L->Length = 0;
return OK;
}
3.测试线性表是否为空
利用顺序表来实现判断线性表是否为空的操作非常简单,只需要判断实际长度是否为0即可。
int EmptyList(SeqList *L){
return (L->Length==0);
}
4.球线性表的长度
返回实际长度 Length
即可
int LengthList(SeqList *L){
return L->Length;
}
5.遍历线性表
遍历线性表就是对线性表中的每一个数据元素访问一次,而且仅访问一次。当访问到每个数据元素时,可以根据需要对该元素进行相应的处理。
void TraverseList(SeqList *L){
int i;
for(i=0; i<L->Length; i++)
printf("%d\t",L->data[i]);
}
6.插入元素
线性表的插入操作是指将某个数据元素e插入到线性表(表长为n)的第 i (1 ≤ i ≤ n+1)个位置上,使其成为新的第i个元素1。过程描述如下:
判断顺序表是否已满。若长度达到了顺序表的分配空间长度或者表长度的最大值(即 Length=ListLength 或 Length=Max_Length),则无法完成插入,返回溢出错误(Overflow)
判断插入位置的合法性。如果插入位置 i 超出了合法位置[1,n+1],则返回插入位置不正确的错误(IllegalPos)。否则执行3
- 从最后一个元素 an 开始,将第 i 到第n个元素之间的所有元素依次逆序后移一个位置,空出第 i 个位置
- 将e插入到空出的第 i 个位置,顺序表的长度加1
Status InsertList(SeqList *L, int i, ElemType e){
int k;
if(L->Length == Max_Length)
return Err_Overflow;
if(i<1 || i>L->Length+1)
return Err_IllegalPos;
for(k=L->Length-1; k>=i-1; k--)
L->data[k+1] = L->data[k];
L->data[i-1] = e;
L->Length++;
return OK;
}
在前面的算法中,如果测试表已满,将不能插入元素。实际上,可以利用动态内存分配技术动态改变顺序表的分配空间,通过为顺序表分配更大的存储空间来完成数据元素的插入。采用动态内存分配的顺序表插入算法如下:
Status InsertList(SeqList *L, int i, ElemType e){
int k;
ElemType *newdata;
if(i<1 || i>L->Length+1)
return Err_IllegalPos;
if(L->Length == Max_Length){
newdata = (ElemType*)realloc(L->data,(L->ListLength + Increment_Length) * sizeof(ElemType));
if(!newdata)
return Err_Memory;
L->data = newdata;
L->ListLength += Increment_Length;
}
for(k=L->Length-1; k>=i-1; k--)
L->data[k+1] = L->data[k];
L->data[i-1] = e;
L->Length++;
return OK;
}
7.删除元素
线性表的删除操作是指将线性表中第 i (1 ≤ i ≤ n+1)个位置的数据元素从表中去掉,并将删除的元素存放到变量e中,同时表的长度减1.过程描述如下
判断线性表是否为空。如果为空,返回错误信息
判断删除位置的合法性。如果输入的删除位置不在有效区间[1,n]范围内,则返回位置非法信息,否则执行3
- 将顺序表第i个元素存放到e中,然后从第i+1到第n个元素之间的所有元素都前移一位,表的长度减1
Status DeleteList(SeqList *L, int i, ElemType *e){
int k;
if(L->Length == 0)
return Err_InvalidParam;
if(i<1 || i>L->Length)
return Err_IllegalPos;
*e = L->data[i-1];
for(k=i; k<L->Length; k++)
L->data[k-1] = L->data[k];
L->Length--;
return OK;
}
8.定位元素
线性表定位操作实际上就是在线性表中查找某个给定的元素e,确定e在表中的位置(通常是第一次出现的位置)。在顺序表中实现定位操作最简单的方法就是从个第一个元素开始,依次与给定的元素e进行比较,直到找到一个与e相等的元素,返回其在顺序表中的位置;或者查遍整个顺序表也没找到与e相等的元素,则返回0,表示没有找到。算法描述如下:
int LocateList(SeqList *L, ElemType e){
int i=0;
while(i<L->Length && L->data[i]!=e)
i++;
if(i<L->Length)
return i+1;
else
return 0;
}
9.获取元素
获取元素操作就是在线性表中返回指定位置上的元素,如果指定位置存在数据元素,则返回其值。算法描述如下:
Status GetElem(SeqList *L, int i, ElemType *e){
if(i<1 || i>L->Length)
return Err_IllegalPos;
*e = L->data[i-1];
return OK;
}
以上就是c语言实现线性表结构及有关操作的代码。