线性表
基础概念
定义:线性表是具有相同特征数据元素的一个有限序列,该序列中所含元素的个数叫线性表的长度。一个线性表中的元素个数n(n>=0)定义为线性表的长度,n=0时称为空表,非空线性表中的每个元素都有一个确定的位序。
线性表中的元素之间的关系是线性关系:
- 存在唯一的一个第一个元素(表头元素,表头元素没有前驱)
- 存在唯一的一个最后一个元素(表尾元素,表尾元素没有后继)
- 除了第一个元素之外,每个元素均只有一个直接前驱
- 除了最后一个元素之外,每个元素均只有一个直接后继
线性表的存储结构:线性表的存储结构有顺序存储结构和链式存储结构两种,前者称为顺序表,后者称为链表。
**顺序表:**顺序表就是把线性表中所有的元素按照其逻辑顺序,依次存储到从指定的存储位置开始的一块连续的存储空间中。
顺序表的特点:(考试中经常会出现的选择题)
- 存储单元地址连续(需要一段连续的空间)
- 逻辑上相邻的数据元素其物理位置也相邻
- 存储密度大(100%)
- 能够随机存取(随机访问)
**链表:**在链表存储中,每个结点不仅包含所有元素的信息,还包含元素之间逻辑关系的信息
链表表的特点:(考试中经常会出现的选择题)
- 链表支持存储空间的动态分配
- 链表不支持随机访问
考试中经常考到的顺序表和链表的比较
- 存储分配方式:顺序表的存储空间是一次性分配的,容易造成存储空间的浪费;链表的存储空间是多次分配的(顺序表必须用一片连续存储单元,链表不需要。)
- 存储密度(存储密度=结点值域所占存储量/结点结构所占的存储总量):顺序表的存储密度=1;链表的存储密度<1(因为结点中有指针域)
- 从整体来看,一般顺序表存储空间利用率低于链表,但从单个存储单元来看,顺序表存储空间利用率高于链表。
- 顺序表可次随机存取,链表不能随机存取
- 插入/删除元素时候,顺序表基本需要移动元素,链表只需要修改指针。
顺序表中基本操作的实现
定义顺序表的存储结构
#define MAXSIZE 100 //顺序表能达到的最大长度
typedef struct{
ElemType *elem; //存储空间的基地址
int length; //顺序表当前长度
}Sqlist;
顺序表初始化(为顺序表L动态分配一个预定义大小的数存储空间;使elem指向这段空间的基地址;将表的当前长度设置为0)
Status InitList(Sqlist &L){
//构造一个空的顺序表L
L.elem = new ElemType[MAXSIZE]; //为顺序表分配一个大小为MAXSIZE的存储空间
if(!L.elem) //判断如果分配失败,退出
exit(OVERFLOW);
L.length = 0; //让空表长度为0
return OK;
}
顺序表的插入(判断插入位置是否合法;判断顺序表的存储空间是否已经满了;将第n至第i个位置的元素依次向后移动一个位置,空出第i个位置;将要插入的新元素e放入第i个位置;将表长自增1)
Status ListInsert(Sqlist &L, int i, ELemType e){
//在顺序表L中的第i个位置插入新元素e
if ((i<1)||(i>L.length+1) //判断输入是否合法
return ERROR;
if (L.length==MAXSIZE) //判断存储空间是否已经满了
return ERROR;
for(j = L.length-1 ;j>=i-1 ;j--)
L.elem[j+1] = L.elem[j];
L.elem[i-1] = e;
++L.length;
return OK;
}
顺序表的删除(判断删除的位置i是否合法;将第i+1个至第n个元素依次向前移动一个位置,表长自减1)
Status Listdelete(Sqlist &L ,int i){
//在顺序表L中删除第i个元素;
if((i<1)||(i>L.length))
return ERROR;
for(j = i; j<=L.length-1 ;j++)
L.elem[j-1] = L.elem[j];
--L.length;
return OK;
}
顺序表的查找(从第一个元素开始,依次和e进行比较,如果找到与e相等的元素L.elem[i],则查找成功)
Status Listsearch(Sqlist L, ElemType e){
//顺序表L中查找数值为e的数据元素,返回序号.Sqlist L不需要使用引用型,可以理解为顺序表L在查找的过程中没有发生改变。
for (i = 0; i< L.length; i++)
if(L.elem[i]==e)
return i+1;
return 0;
}
顺序表的取值(判断指定的位置序号i是否合法;合理将第i个数据元素L.elem[i-1]赋值给参数e;通过e返回第i个数据算的传值)
Status GetElem(Sqlist L, int i,Elemtype &e){
//因为e要发生改变,所以使用引用型
if(i<1||i>L.length)
return ERROR;
e = L.elem[i-1];
return OK;
}
顺序表的算法融合练习
#include <iostream>
using namespace std;
typedef int Status;
typedef int ElemType;
#define OVERFLOW 3
#define ERROR 0
#define OK 1
//顺序表的存储结构
#define MAXSIZE 100
typedef struct
{
ElemType *elem;
int length;
}SqList;
SqList L;
//初始化顺序表
Status InitList(SqList &L)
{//构造一个空的顺序表L
L.elem = new ElemType[MAXSIZE];
if(!L.elem)
exit(OVERFLOW);
L.length = 0;
return OK;
}
//顺序表销毁
Status DestroyList(SqList &L)
{
delete []L.elem;
L.elem = NULL;
L.length = 0;
return OK;
}
//顺序表清空
Status ClearList(SqList &L)
{
L.length = 0;
return OK;
}
//顺序表空表
Status ListEmpty(SqList L)
{
if(L.length == 0)
return OK;
return ERROR;
}
//顺序表表长
Status ListLength(SqList L)
{
return L.length;
}
//顺序表取值 O(1)
Status GetElem(SqList L, int i, ElemType &e)
{
if(i < 1 || i > L.length) //判断i值是否合理,若不合理,返回ERROR
return ERROR;
e = L.elem[i - 1]; //elem[i - 1]储存第i个数据元素
return OK;
}
//顺序表查找 O(n)
Status LocateElem(SqList L, ElemType e)
{//顺序表中查找值为e的元素,返回其序号
for(int i = 0; i < L.length; i++)
if(L.elem[i] == e)
return i + 1;
return ERROR;
}
//顺序表前驱
Status PriorElem(SqList L, ElemType cur_e, ElemType &pre_e)
{
for(int i = 0; i < L.length; i++)
if(L.elem[i] == cur_e)
{
pre_e = L.elem[i - 1];
return OK;
}
return ERROR;
}
//顺序表后继
Status NextElem(SqList L, ElemType cur_e, ElemType &next_e)
{
for(int i = 0; i < L.length; i++)
if(L.elem[i] == cur_e)
{
next_e = L.elem[i + 1];
return OK;
}
return ERROR;
}
//顺序表插入 O(n)
Status ListInsert(SqList &L, int i, ElemType e)
{//在顺序表L中第i个位置之前插入新的元素e, i值的合法范围是1 <= i <= L.length+1
if(i < 1 || i > L.length + 1) //i值不合法
return ERROR;
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;
}
//顺序表删除 O(n)
Status ListDelete(SqList &L, int i)
{//在顺序表L中删除第i个元素, i值的合法范围是1<=i<=L.length
if(i < 1 || i > L.length)
return ERROR; //i值不合法
for(int j = i; j <= L.length - 1; j++)
L.elem[j - 1] = L.elem[j]; //被删除元素之后的元素前移
L.length--; //表长减1
return OK;
}
//顺序表遍历 O(n)
Status TraverseList(SqList L)
{
for(int i = 0; i < L.length; i++)
{
if(i == L.length - 1)
cout<<L.elem[i]<<'\n';
else
cout<<L.elem[i]<<' ';
}
return OK;
}
int main()
{
cout<<"----------------------顺序表----------------------"<<'\n';
cout<<"操作0:退出程序"<<'\n';
cout<<"操作1:建立一个顺序表,输入n个元素并输出"<<'\n';
cout<<"操作2:查找线性表中的最大元素并输出"<<'\n';
cout<<"操作3:在线性表的第i个元素前插入一个正整数x"<<'\n';
cout<<"操作4:删除线性表中的第j个元素"<<'\n';
cout<<"操作5:将线性表中的元素按升序排列"<<'\n';
cout<<"操作6:将线性表中的元素就地逆序(只允许用一个暂存单元)"<<'\n';
cout<<"--------------------------------------------------"<<'\n';
int a, n, flag = 1;
while(flag)
{
cout<<'\n'<<"请选择要执行的操作:";
cin>>a;
switch(a)
{
case 0:
{
cout<<"结束!"<<'\n';
flag = 0;
break;
}
case 1:
{
cout<<"请输入要建立的顺序表的元素个数:";
cin>>n;
cout<<"请按顺序输入这"<<n<<"个元素:";
ElemType e;
InitList(L);
for(int i = 1; i <= n; i++)
{
cin>>e;
ListInsert(L, i, e);
}
cout<<"顺序表创建完毕:";
TraverseList(L);
break;
}
case 2:
{
int maxIndex = 0;
for(int i = 1; i < L.length; i++)
if(L.elem[maxIndex] < L.elem[i])
maxIndex = i;
cout<<"线性表中的最大元素为:"<<L.elem[maxIndex]<<'\n';
break;
}
case 3:
{
int i, x;
cout<<"在线性表的第i个元素前插入一个正整数x,请依次输入i和x的值:";
cin>>i>>x;
if(ListInsert(L, i, x))
{
cout<<"顺序表插入完毕:";
TraverseList(L);
}
else
cout<<"顺序表插入不合法!"<<'\n';
break;
}
case 4:
{
int j;
cout<<"删除线性表中的第j个元素,请输入j的值:";
cin>>j;
if(ListDelete(L, j))
{
cout<<"顺序表删除完毕:";
TraverseList(L);
}
else
cout<<"顺序表删除不合法!"<<'\n';
break;
}
case 5:
{
for(int i = 0; i < L.length - 1; i++)
{
int minIndex = i;
for(int j = i; j < L.length - 1; j++)
{
if(L.elem[minIndex] > L.elem[j + 1])
minIndex = j + 1;
}
int tmp = L.elem[i];
L.elem[i] = L.elem[minIndex];
L.elem[minIndex] = tmp;
}
cout<<"顺序表升序排列完毕:";
TraverseList(L);
break;
}
case 6:
{
for(int i = 0; i < L.length / 2; i++)
{
int tmp = L.elem[i];
L.elem[i] = L.elem[L.length - i - 1];
L.elem[L.length - i - 1] = tmp;
}
cout<<"顺序表就地逆序完毕:";
TraverseList(L);
break;
}
}
}
return 0;
}
未完待续。。。。
本文详细介绍了线性表的基础概念,包括线性表的定义、特点和两种主要的存储结构——顺序表和链表。顺序表采用连续的存储空间,支持随机访问,而链表则利用指针链接元素,便于动态扩展。文中还提供了顺序表的C++实现,包括初始化、插入、删除、查找等基本操作,并展示了如何在实际应用中使用这些操作。
441





