数据结构—线性表的顺序表示
程序员文章站
2022-03-15 10:14:03
...
1.相关概念
- 线性表的顺序表示又称为顺序存储结构或顺序映像。
- 顺序存储定义:把逻辑上相邻的数据元素存储在物理上相邻的存储单元中的存储结构。
- 简言之,逻辑上相邻,物理上也相邻。
- 顺序存储方法:用一组地址连续的存储单元依次存储线性表的元素,可通过数组V[n]来实现。
2.顺序表的类型定义
//--------顺序表的存储结构----
#define MAX 100 //顺序表可能达到的最大长度
typedef struct Sq
{
Elemtype *elem; //存储空间的基地址
int length; //当前长度
}SqList;
/*Elemtype是一个抽象数据类型 ,可以是int,float,double等或者是自定义的数据类型。
在实际使用是可以使用int,float等来代替;
*/
3.线性表的重要基本操作
3.1顺序表的初始化
-
算法步骤
- ①为顺序表L动态分配一个预定义大小的数组空间,使elem指向这段空间的基地址。
- ②将表的当前长度设为0
- 算法描述
Status InitSqList_Sq(SqList &L)
{
//构造一个空的顺序表L
L.elem=new Elemtype[MAX]; //为顺序表分配一个MAX大小的数组空间
if(!L.elem)
exit(OVERFLOW); //存储分配失败
L.length=0; //空表长度为0
return OK;
}
3.2 顺序表的取值
-
算法步骤
- ①判断指定的位置序号i值是否合理(1≤i≤ L.length),若不合理,则返回 ERROR。
- ②若i值合理,则将第i个数据元素 L.elem[i-l]赋给参数e,通过e返回第i个数据元素的传值。
- 算法描述
Status GetElem(SqList L, int i, Book &e)
{
if (i < 1 || i > L.length)
return ERROR; // 判断i值是否合理,若不合理,返回ERROR
e = L.elem[i - 1]; // elem[i-1]单元存储第i个数据元素
return OK;
}
3.3顺序表的查找
-
算法步骤
- ①从第一个元素起,依次和e相比较,若找到与e相等的元素 L.elem[i],则查找成功,返回该元素的序号i+1
- ②若查遍整个顺序表都没有找到,则查找失败,返回0
- 算法描述
int LocationElem_Sq(SqList L,Elemtype e)
{
for(int i=0;i<L.length;i++)
if(L.elem[i]==e)
return i+1;
return 0;
}
3.4 顺序表的插入
- 算法步骤
- ①判断插入位置i是否合法(i值的合法范围是1≤i≤n+1),若不合法则返回ERROR
- ②判断顺序表的存储空间是否已满,若满则返回ERROR。
- ③将第n个至第i个位置的元素依次向后移动一个位置,空出第i个位置(i=n+1时无需多动)
- ④将要插入的新元素e放入第i个位置。
- ⑤表长加1。
- 算法描述:
Status ListInsert_Sq(SqList &L, int i, Elemtype e)
{
//在顺序表L中第i个位置之前插入新的元素e,i值的合法范围是1<=i<=L.length+1
if ((i < 1) || (i > L.length + 1))
return ERROR; // i值不合法
if (L.length == MAXSIZE)
return ERROR; // 当前存储空间已满
for (int j = L.length - 1; j >= i - 1; j--)
L.elem[j + 1] = L.elem[j]; //插入位置及之后的元素后移
L.elem[i - 1] = e; // 将新元素e放入第i个位置
L.length++; // 表长增1
return OK;
}
3..5 顺序表的删除
- 算法步骤
- ①判断删除位置i是否合法(合法值为1≤i≤n),若不合法则返回 ERROR
- ②将第i+1个至第n个的元素依次向前移动一个位置(i=n时无需移动)
- ③表长减1。
- 算法描述:
Status ListDelete_Sq(SqList &L, int i)
{
//在顺序表L中删除第i个元素,并用e返回其值
//i值的合法范围是1<=i<=L.length
if ((i < 1) || (i > L.length))
return ERROR; //i值不合法
for (int j = i; j <= L.length; j++)
L.elem[j - 1] = L.elem[j]; //被删除元素之后的元素前移
L.length--; //表长减1
return OK;
}
4.算法分析
- 对于插入操作:
- 算法时间主要耗费在移动元素的操作上
- 若插入在尾结点之后,则根本无需移动(特别快); 若插入在首结点之前,则表中元素全部后移(特别慢); 若要考虑在各种位置插入(共n+1种可能)的平均移动次数,该如何计算?
- 对于删除操作:
- 算法时间主要耗费在移动元素的操作上
- 若删除尾结点,则根本无需移动(特别快); 若删除首结点,则表中n-1个元素全部前移(特别慢); 若要考虑在各种位置删除(共n种可能)的平均移动次数,该如何计算?
- 查找、插入、删除算法的平均时间复杂度为 O(n),取值操作平均时间复杂度为 O(1)
- 顺序表的空间复杂度S(n)=O(1) (没有占用辅助空间)
5.总结
- 顺序表(顺序存储结构)的特点:
- 利用数据元素的存储位置表示线性表中相邻数据元素之间的前后关系,即线性表的逻辑结构与存储结构一致
- 在访问线性表时,可以快速地计算出任何一个数据元素的存储地址。因此可以粗略地认为,访问每个元素所花时间相等
- 顺序表的优缺点:
- 优点: 存储密度大(结点本身所占存储量/结点结构所占存储量),可以随机存取表中任一元素
- 缺点:
- 在插入、删除某一元素时,需要移动大量元素
- 浪费存储空间
- 属于静态存储形式,数据元素的个数不能*扩充
代码实现:
- main.cpp
#include<iostream>
using namespace std;
#define MAX 100
typedef struct Sq
{
int *elem;
int length;
}SqList;
// 初始化表
int InitSqList(SqList &L)
{
L.elem = new int[MAX];
if (!L.elem)
{
return 0;
}
else
{
L.length = 0;
return 1;
}
}
//判断表是否为空
int ListEmpty(SqList L)
{
if (L.length != 0)
{
return 1; //表存在且表长不为0,返回非空
}
else if (L.length == 0)
{
return 0;//表为空
}
}
//计算表长
int ListLength(SqList L)
{
return L.length;
}
// 输入操作
void Input(SqList &L)
{
int e;
int Loop = 1;
printf("请输入表的元素值(输入0结束):\n");
int i = 0;
while (Loop)
{
scanf("%d", &e);
if (e != 0)
{
L.elem[i++] = e;
L.length = i;
}
else
{
Loop = 0;
}
}
printf("输入结束!\n");
}
// 输出线性表中的所有值
void Output(SqList L)
{
printf("顺序表中的元素值为:\n");
for (int i = 0; i < L.length - 1; i++)
{
printf("%d ", L.elem[i]);
}
printf("%d\n", L.elem[L.length - 1]);
}
//查询指定位置的元素的值
void SelectByNo(SqList &L, int location)
{
//首先判断表是否为空
if (location <= 0 || location > L.length)
{
printf("输入错误!\n");
}
else if (!ListEmpty(L))
{
printf("表为空,查找失败!\n");
}
else
{
printf("待查找元素的值:%d\n", L.elem[location - 1]);
}
}
// 查询某个元素在表中的位置
void SelectByValue(SqList L, int value)
{
//首先判断表是否为空
if (!ListEmpty(L))
{
printf("表为空!\n");
}
else
{
for (int i = 0; i < L.length; i++)
{
if (L.elem[i] == value)
{
printf("%d在表中的位置:%d\n", value, i + 1);
}
}
}
}
//查找某个值的前驱
void PriorElem(SqList L, int value)
{
//遍历表,判断value是否是表的元素
//且表的第一个元素是否==value,表的最后一个元素没有前驱
int count = 0;
for (int i = 0; i < L.length; i++)
{
if (L.elem[i] == value)
{
count++;
if (L.elem[0] == value)
{
printf("%d是表的第一个元素,没有前驱.\n", value);
}
else
{
printf("%d的前驱是:%d\n", value, L.elem[i - 1]);
}
}
}
if (count == 0)
{
printf("%d不是表中的元素!\n", value);
}
}
// 查找某个值的后继
void NextElem(SqList L, int value)
{
//遍历表,判断vale是否是表中的元素
//且是否是表的最后一个元素,最后一个元素没有后继
int count = 0;
for (int i = 0; i < L.length; i++)
{
if (L.elem[i] == value)
{
count++;
if (L.elem[L.length - 1] == value)
{
printf("%d是表的最后一个元素,没有后继.\n", value);
}
else
{
printf("%d的后继是:%d\n", value, L.elem[i + 1]);
}
}
}
if (count == 0)
{
printf("%d不是表中的元素.\n", value);
}
}
/*
插入操作
在L中第i个位置之前插入元素value
插入后,表长加1,第i及以后的元素位置向后移动1个
*/
void ListInsert(SqList &L, int location, int value)
{
//首先判断location的位置是否合法
if (location<1 || location>L.length + 1)
{
printf("插入位置不合法!\n");
}
else
{
for (int i = L.length - 1; i >= location - 1; i--)
{
L.elem[i + 1] = L.elem[i];
}
L.elem[location - 1] = value;
L.length++;
printf("插入成功!\n");
}
}
/*
删除操作
删除指定位置的元素,表长减1
location及其后的元素的位置向前移动1个
*/
void ListDelete(SqList &L, int location)
{
//首先判断location是否合法
if (location < 1 || location >= L.length + 1)
{
printf("要删除的元素不存在!\n");
}
else
{
printf("要删除的元素的值是:%d\n", L.elem[location - 1]);
for (int i = location; i <= L.length - 1; i++)
{
L.elem[i - 1] = L.elem[i];
}
L.length--;
printf("删除成功!\n");
}
}
int main()
{
SqList L;
//InitSqList(L);
printf("顺序表的实现和操作:\n");
printf("1.建立顺序表\n");
printf("2.输入表的元素\n");
printf("3.输出表的元素\n");
printf("4.插入操作\n");
printf("5.删除操作\n");
printf("6.查询\n");
printf("0.退出\n");
int choose = -1;
while (choose != 0)
{
printf("请选择:\n");
scanf("%d", &choose);
switch (choose)
{
case 1:
if (InitSqList(L))
{
printf("顺序表初始化成功!\n");
}
else
{
printf("初始化失败!");
}
break;
case 2:
Input(L);
break;
case 3:
Output(L);
break;
case 4:
int location, value;
printf("请输入插入的位置和插入元素的数值:\n");
scanf("%d %d", &location, &value);
ListInsert(L, location, value);
printf("插入后的表是:\n");
Output(L);
break;
case 5:
printf("请输入要删除的元素在表中的位置:\n");
int location1;
scanf("%d", &location1);
ListDelete(L, location1);
printf("删除后的表为:\n");
Output(L);
break;
case 6:
printf("1.查找指定元素的前驱\n");
printf("2.查找指定元素的后继\n");
printf("3.按序号查询某个元素的值\n");
printf("4.按值查询某个元素的位置\n");
printf("请选择:\n");
int choose;
scanf("%d", &choose);
switch (choose)
{
case 1:
int value;
printf("请输入元素值:\n");
scanf("%d", &value);
PriorElem(L, value);
break;
case 2:
int value1;
printf("请输入元素的值:\n");
scanf("%d", &value1);
NextElem(L, value1);
break;
case 3:
printf("输入要查询元素的序号:\n");
int location;
scanf("%d", &location);
SelectByNo(L, location);
break;
case 4:
printf("输入要查询元素的值:\n");
int value2;
scanf("%d", &value2);
SelectByValue(L, value2);
break;
}
break;
case 0:
printf("退出成功!\n");
break;
}
}
return 0;
}