线性表的顺序表示
〇、顺序表定义
用一组地址连续的存储单元一次存储线性表的数据元素。特点是:逻辑上相邻的数据元素,其物理次序也是相邻的。
一、顺序表的存储结构
C/C++
中常用数组来描述数据结构中的顺序存储结构,由于线性表的长度可变,且所需的最大存储空间随问题不同而不同,则可以用动态分配的一维数组表示线性表,如下所示
#define MAXSIZE 100 //顺序表可能达到的最大长度
typedef struct
{
ElemType* elem; //存储空间的基地址
int length; //当前长度
}SqList;
其中Elemtype
是为了描述统一而自定的抽象数据类型,具体如下所示:
1.1 表示稀疏多项式数据
#define MAXSIZE 100
typedef struct //多项式中非零项的定义
{
float coef; //系数
int expn; //指数
}Polynomial;
typedef struct
{
Polynomial* elem; //存储空间的基地址
int length; //多项式中的项数
}SqList;
1.2 表示图书馆的书籍
#define MAXSIZE 50000 //能记录的最多种类数
typedef struct //图书信息的定义
{
char ISBN[20]; //ISBN号码
char name[50]; //书名
float price; //书的价格
}Book;
typedef struct
{
Book* elem; //存储空间的基地址
unsigned int length;//当前图书总数
}SqList;
二、基本操作的实现
2.0 先声明一些状态
后面直接使用,不再加以说明
#define Status int
enum MyStatus
{
ERROR = 0,\
OK = 1,\
FALSE = 0,\
TURE = 1
};
2.1 初始化
顺序表的初始化就是构建一个空的顺序表
2.1.1 算法步骤
- 为顺序表
L
分配一个预定义大小的数组空间,使elem
指向这段空间的基地址- 将表的当前长度设置为
0
2.1.2 算法实现
/*顺序表的初始化*/
Status InitList(SqList* L) //构建一个空的顺序表L
{
L -> elem = new ElemType(MAXSIZE); //为其分配一个大小为MAXSIZE的内存空间
L->length = 0; //空表长度为0
return OK; //成功返回OK
if (! L->elem)
{
return ERROR; //如果分配失败返回ERROR
}
}
2.2 取值
取值操作就是根据指定的位置序号i
,获取顺序表中的第i
个数据元素的值。由于顺序存储结构具有随机存取的特点,可以直接通过数组下标定位得到,我们可以知道,数组elem[i - 1]
单元存储第i
个数据元素。
2.2.1 算法步骤
- 判断指定的位置序号
i
值是否合理(1 ≤ i ≤ L.length)
,若不合理则返回ERROR
- 若
i
合理,则将第i
个元素L.elem[i - 1]
赋给参数e
,通过e
返回第i个数据元素的传值
2.2.2 算法实现
/*顺序表的取值*/
Status GetElem(SqList L, const int* i, ElemType* e)
{
if (*i < 1 || *i > L.length) //判断i是否合理
{
return ERROR;
}
else
{
*e = L.elem[(*i) - 1]; //取第i个值
return OK;
}
}
2.2.3 复杂度分析
顺序表取值算法的时间复杂度为O(1)
2.3 查找
查找操作就是根据指定的元素e
,找出顺序表中第一个与e
相等的元素。若查找成功,则读出该元素在表中的位置序号传给num
;否则返回ERROR
2.3.1 算法步骤
- 对元素进行遍历,如果找到与
e
相等的元素,将其序号传给num
- 若没找到,返回
ERROR
2.3.2 算法实现
/*顺序表的查找*/
Status LocateElem(SqList L, const ElemType* e, int* num)
{
*num = 0;
for (int i = 0; i < L.length; i++)//遍历
{
if (*e == L.elem[i])
{
*num = i + 1; //查找到将位置传递出去
}
}
return !(*num) ? OK : ERROR; //返回状态值
}
2.3.3 复杂度分析
顺序表查找算法的时间复杂度为O(n)
2.4 插入
顺序表的插入操作是指在表的第i
个位置插入一个新的数据元素e
,使长度为L.length
的顺序表变为长度为L.length + 1
的顺序表
2.4.1 算法步骤
- 判断插入位置
i
是否合法(1 ≤ i ≤ L.length + 1)
,若不合法则返回ERROR
- 判断顺序表的存储空间是否已满,若满则返回
ERROR
- 将第
i
到第L.length
个元素向右移动一个位置,空出第i
个位置(i
为L.length + 1
时不用移动)- 将要插入的新元素
e
放入第i
个位置- 顺序表的长度
L.length
加1
2.4.2 算法实现
/*顺序表的插入*/
Status ListInsert(SqList* L, const int* i, const ElemType* e)
{
if (L->length == MAXSIZE || (*i) < 1 || (*i) > L->length + 1)
{//已经没有存储空间或i的值不合法
return ERROR;
}
else
{
if ((*i) == L->length + 1) //i在表尾
{
L->elem[L->length] = *e;//直接放入表尾
}
else//i合法且不在表尾
{
for (int j = L->length - 1; j >= i - 1; j--)
{//第i个元素开始全部后移动
L->elem[j + 1] = L->elem[j];
}
L->elem[(*i) - 1] = *e; //插入
}
L->length++;
return OK;
}
}
2.4.3 复杂度分析
顺序表插入算法的时间复杂度为O(n)
2.5 删除
顺序表的删除操作是指在表的第i
个位置删除一个数据元素e
,使长度为L.length
的顺序表变为长度为L.length - 1
的顺序表
2.5.1 算法步骤
- 判断
i
是否合法(1 ≤ i ≤ L.length)
,若不合法则返回ERROR
- 读出这个数据元素
e
- 将顺序表第
i + 1
到第L.length
个元素向左移动一个位置,覆盖第i
个位置的元素(i
为L.length
时不用移动)- 顺序表的长度
L.length
减1
2.5.2 算法实现
/*顺序表的删除*/
Status ListDelete(SqList* L, const int* i, ElemType* e)
{
if ((*i) < 1 || (*i) > L->length)//i的值非法
{
return ERROR;
}
else
{
*e = L->elem[(*i) - 1];//取出第i个值
for (int j = (*i); j < L->length - 1; j++)
{//第i个值以后的元素均向前移一位
L->elem[j - 1] = L->elem[j];
}
L->length--;//长度减一
return OK;
}
}
2.5.3 复杂度分析
顺序表删除算法的时间复杂度为O(n)
三、顺序表的优劣
3.1 优点
- 逻辑结构和物理结构相关联,无需为表示元素间的逻辑关系增加额外的代码量和储存空间
- 可以快速存取某一位置的元素
3.2 缺点
- 插入和删除操作需要移动大量的元素
- 线性表长度变化很大时难以确定储存空间的容量
- 容易造成物理存储空间的碎片化
四、测试
4.1 测试要求
- 创建一个内存为
10
的顺序表- 对其赋
8
个值,分别为10 28 36 15 20 11 5 3
- 取出第
4
个值并打印出来- 打印出
11
的位置- 在第
5
元素后插入22
,打印出插入后的顺序表- 删除第
2
个元素并打印出其值以及删除后的顺序表- 删除整个顺序表
4.2测试代码
以C语言为主,代码稍微有那么点点长
#include <iostream>
#include <malloc.h>
using namespace std;
#define MAXSIZE 10 //顺序表可能达到的最大长度
#define ElemType int
#define Status int
enum MyStatus
{
ERROR = 0, \
OK = 1, \
FALSE = 0, \
TURE = 1
};
/***顺序表的存储结构***/
typedef struct
{
ElemType* elem; //存储空间的基地址
int length; //当前长度
}SqList;
SqList List;
/*顺序表的初始化*/
Status InitList(SqList* L);
/*顺序表的取值*/
Status GetElem(SqList L, const int* i, ElemType* e);
/*顺序表的查找*/
Status LocateElem(SqList L, const ElemType* e, int* num);
/*顺序表的插入*/
Status ListInsert(SqList* L, const int* i, const ElemType* e);
/*顺序表的删除*/
Status ListDelete(SqList* L, const int* i, ElemType* e);
//输出线性表
void print(SqList* L);
//空线性表赋值
Status ConfigList(SqList* L, int i);
/*顺序表的删除*/
Status Delete(SqList* L);
int main(void)
{
cout << "正在创建顺序表\n";
int flag = InitList(&List);
if (flag)
{
cout << "顺序表创建完毕" << "请进行赋值" << endl;
}
for (int i = 0; i < 8; i++)
{
flag = ConfigList(&List, i);
}
if (flag)
{
cout << "\n赋值完毕,当前顺序表中的内容为: ";
print(&List);
}
int temp = 4;
ElemType e;
flag = GetElem(List, &temp, &e);
if (flag)
{
cout << "读取成功:第4个值为" << e << endl;
}
else
{
cout << "读取失败" << endl;
}
e = 11;
int num;
flag = LocateElem(List, &e, &num);
if (flag)
{
cout << "查找成功,第: " << num << "个值为11" << endl << endl;
}
else
{
cout << "查找失败" << endl;
}
temp = 5;
e = 22;
flag = ListInsert(&List, &temp, &e);
if (flag)
{
cout << "在第5元素前插入22后的顺序表为:";
print(&List);
}
else
{
cout << "插入失败" << endl;
}
temp = 2;
flag = ListDelete(&List, &temp, &e);
if (flag)
{
cout << "删除成功,删除的元素为:" << e << endl << "成功删除第2个元素后的顺序表为:";
print(&List);
}
else
{
cout << "删除元素失败" << endl;
}
flag = Delete(&List);
if (flag)
{
cout << "顺序表已删除" << endl;
}
else
{
cout << "删除顺序表失败" << endl;
}
return 0;
}
/*顺序表的初始化*/
Status InitList(SqList* L) //构建一个空的顺序表L
{
//为其分配内存
L->elem = (ElemType*)malloc(sizeof(ElemType) * MAXSIZE);
L->length = 0; //空表长度为0
return OK; //成功返回OK
if (!L->elem)
{
return ERROR; //如果分配失败返回ERROR
}
}
/*顺序表的取值*/
Status GetElem(SqList L, const int* i, ElemType* e)
{
if (*i < 1 || *i > L.length) //判断i是否合理
{
return ERROR;
}
else
{
*e = L.elem[(*i) - 1]; //取第i个值
return OK;
}
}
/*顺序表的查找*/
Status LocateElem(SqList L, const ElemType* e, int* num)
{
*num = 0;
for (int i = 0; i < L.length; i++)//遍历
{
if (*e == L.elem[i])
{
*num = i + 1; //查找到将位置传递出去
return OK;
}
}
return ERROR;
}
/*顺序表的插入*/
Status ListInsert(SqList* L, const int* i, const ElemType* e)
{
if (L->length == MAXSIZE || (*i) < 1 || (*i) > L->length + 1)
{ //已经没有存储空间或i的值不合法
return ERROR;
}
else
{
if ((*i) == L->length + 1) //i在表尾
{
L->elem[L->length] = *e;//直接放入表尾
}
else //i合法且不在表尾
{
for (int j = L->length - 1; j >= (*i) - 1; j--)
{ //第i个元素开始全部后移动
L->elem[j + 1] = L->elem[j];
}
L->elem[(*i) - 1] = *e; //插入
}
L->length++;
return OK;
}
}
/*顺序表的删除*/
Status ListDelete(SqList* L, const int* i, ElemType* e)
{
if ((*i) < 1 || (*i) > L->length)//i的值非法
{
return ERROR;
}
else
{
*e = L->elem[(*i) - 1];//取出第i个值
for (int j = (*i); j < L->length; j++)
{//第i个值以后的元素均向前移一位
L->elem[j - 1] = L->elem[j];
}
L->length--;//长度减一
return OK;
}
}
//输出线性表元素
void print(SqList* L) {
for (int i = 0; i < L->length; i++)
{
cout << L->elem[i] << " ";
}
cout << "共" << L->length << "个元素\n\n";
}
//空线性表赋值
Status ConfigList(SqList* L, int i)
{
ElemType scanfnum;
cin >> scanfnum;
L->elem[i] = scanfnum;
L->length++;
return OK;
}
/*顺序表的删除*/
Status Delete(SqList* L) //删除顺序表L
{
free(L->elem); //释放内存
L->elem = NULL;
L->length = 0; //长度置为0
if (! L->elem)
{
return OK; //成功
}
return ERROR; //失败
}