线性表(线性存储结构)线性表是最基本、最简单、也是最常用的一种数据结构。它是数据结构与算法的基础之一。例如26个英文字母表:(A,B,C,D,···,Z)或者学生基本信息表又或者十二星座的排序等等,它们都是线性表。
一.线性表的定义和特点
线性表(linear
list):是n个具有相同特性的数据元素的有限序列
。数据元素是一个抽象的符号,其具体含义在不同的情况下一般不同。
n的个数代表线性表的长度,当n = 0时,称为空表。
对于非空的线性表或线性结构,其特点是:
- 存在唯一的一个被称作“第一个”的数据元素;
- 存在唯一的一个被称作“最后一个”的数据元素;
- 除第一个之外,结构中的每个数据元素均只有一个前驱;
- 除最后一个之外,结构中的每个元素均只有一个后继;
对于前驱和后继
某一元素的左侧相邻元素称为“直接前驱”,位于此元素左侧的所有元素都统称为“前驱元素”;
某一元素的右侧相邻元素称为“直接后继”,位于此元素右侧的所有元素都统称为“后继元素”;
以图数据中的元素 3 来说,它的直接前驱是 2 ,此元素的前驱元素有 2 个,分别是 1 和 2;同理,此元素的直接后继是 4 ,后继元素也有 2 个,分别是 4 和 5。如图所示:
二.线性表的基本操作
InitList(&L)
:构造一个空的线性表L。DestroyList(&L)
:销毁线性表L。ClearList(&L)
:将线性表L重置为空表。ListEmpty(&L)
:若L为空表,则返回true,否则返回false。ListLength(&L)
:返回L中的数据元素的个数。GetElem(L,i,&e)
:用e返回L中第i个数据元素的值。LocateElem(L,e)
:返回L中第一个值与e相同的元素在L中的位置。若此数据元素不存在,则返回值为0。PrioElem(L,cur_e,&pre_e)
:若cur_e是L的数据元素,且不是第一个,则用pre_e返回其前驱,否则操作失败,pre_e无定义。NextElem(L,cur_e,&next_e)
:若cur_e是L的数据元素,且不是最后一个,则用next_e返回其后继,否则操作失败,next_e无定义。ListInsert(&L,i,e)
:在L中第i个位置之前 插入新的元素e,L的长度加1。ListDelet(&L,I)
:删除L的第i个数据元素,L的长度减1。TraverseList(L)
:对线性表L进行遍历,在遍历过程中对L的每一个结点访问一次。
线性表根据在计算机的储存方式可以分为两种:
- 顺序线性表
- 链式线性表
三.线性表的顺序表示和实现
1.顺序表的定义
- 所谓顺序表就是顺序存储的线性表。顺序存储是用一组地址连续的存储单元依次存放线性表中各个元素的存储结构。
2.顺序表的特点
- 在线性表中逻辑上相邻的数据元素,在物理存储上也是相邻的。
- 存储密度高,但要预先分配“足够应用”的存储空间,这可能会造成存储空间的浪费。
- 便于随机存储。
- 不便于插入和删除操作,这是因为在顺序表上进行的插入和删除操作会引起大量数据元素的移动。
例如,使用顺序表存储集合 {1,2,3,4,5}
数据最终的存储状态如图所示
线性表的顺序存储的结构代码:
#define MAXSIZE 20 //存储空间初始分配量
typedef int ElemType;//ElemType根据实际情况而定,这里假设为int
typedef struct
{
ElemType data[MAXSIZE];//数组存储数据元素,最大值为MAXSIZE
int length;//线性表当前长度
}SqList;
描述顺序存储结构需要三个属性:
- 存储空间的起始位置:数组data,它的存储位置就是存储空间的存储位置。
- 线性表的最大存储容量:数组长度MaxSize。
- 线性表的当前长度:length。
线性表长度和数组长度的区别:
数组长度是存放线性表的存储空间的长度,存储空间分配完一般是不变的。
线性表长度是线性表中元素数据的个数,随着线性表插入和删除操作的进行,这个量是变化的。
在任意时刻,线性表的长度应该小于等于数组的长度。
3.顺序表中基本操作的实现
3.1 初始化
顺序表的初始化操作就是构造一个空的顺序表
算法:顺序表的初始化
status InitList(SqList &L)
{//构造一个空的顺序表L
L.elem = new ElemType[MAXSIZE]; //为顺序表分配一个大小为MAXSIZE的数组空间
if(!L.elem) exit(OVERFLOW); //存储分配失败退出
L.lengh = 0; //空表长度为0;
return OK;
3.2 取值
取值操作是根据指定的位置序号i,获取顺序表中的第i个数据元素的值。
算法:顺序表的取值
Status GetElem(SqList L,int i, ElemType &e)
{
if (i<1||i>L.length) return ERROR; //判断i值是否合理,若不合理,则返回ERROR
e = L.elem[i-1]; //elem[i-1]单元存储第i个数组元素
return OK;
}
该算法的时间复杂度为O(1)。
3.3 查找
查找顺序表中第一个值与e相同的元素在L中的位置。若此数据元素不存在,则返回值为0;存在即返回位置序号。
算法:顺序表的查找
int LocateElem(SqList L, ElemType e)
{
//在顺序表L中查找值为e的数据元素,返回其序号
for(i = 0; i < L.length; i++)
if(L.elem[i] == e) {
return i+1; // 查找成功,返回序号+1
}
return 0; //查找失败,返回0
}
3.4 顺序表的插入
顺序表中插入数据元素,无非三种情况:
- 在表头插入;
- 在表的中间某个位置插入;
- 直接尾随顺序表,作为表的最后一个元素;
无论在顺序表的什么位置插入数据元素,解决办法都是:找到要插入的位置,将后续数据元素整体向后移动一个位置,最后直接在腾出来的位置上插入指定的数据元素。
Status ListInsert(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(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)。
3.5 顺序表的删除
从顺序表中删除指定元素,实现起来非常简单,只需找到目标元素,并将其后续所有元素整体前移 1 个位置,表长减1。
后续元素整体前移一个位置,会直接将目标元素删除,可间接实现删除元素的目的。
Status ListDelete(SqList &L,int i)
{
//在顺序表L中删除第i个元素,i值的合法范围是1<=i<=L.length
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; //表长减1
return OK;
}
}
接下篇
数据结构基础(二)——线性表之链式存储结构