线性表基础概念,线性表的顺序表表示和实现
下面的代码片段我都以完整的C++程序编写了下来,详情请看C++实现顺序表
线性表
线性表(linear list)是最基本、最简单、也是最常用的一种数据结构。线性表是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列。
-
特征
线性表是具有相同特征的数据元素的一个有限序列。
线性表(linear list)由n(n>=0)个数据元素(结点)a1,a2,…,an组成的有限序列
线性表中元素的个数n(n≥0)称为线性表的长度,n=0时称为空表。
对于非空的线性表或线性结构,特点为:1.存在唯一的一个被称作"第一个"的数据元素;
2.存在唯一的一个被称作"最后一个"的数据元素;
3.除第一个之外,结构中的每个数据元素均只有一个前驱;
4.除最后一个之外,结构中的每个数据元素均只有一个后继。此图更直观的描述它们之间的关系:
线性表是一种典型的线性结构 -
类型定义
抽象数据类型线性表的定义如下:
基本操作具体:(下面是一些常用操作函数名词,函数内容需具体定义)- InitList(&L) (Initialization List)初始
//构造一个空的线性表L - DestroyList(&L) 销毁
//销毁线性表L - ClearList(&L) 清空
//将线性表L重置为空表 - ListEmpty(L)
//判断线性表L是否为空表,若为空表则返回TURE;否则返回FALSE - ListLength(L) 计算长度
//返回线性表L中的数据元素个数 - GetElem(L,i,&e) 获取元素
//条件:1< = i < = ListLength(L)
//用e返回线性表L中第i个数据元素的值 - LocateElem(L,e)
//返回L中第1个值与e相同的元素在L中的位置。若这样的元素不存在,则返回值为0 - PriorElem(L,cue_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的长度加一 - ListDelete(&L,i) 删除元素
//删除L的第i个数据元素,L的长度减一 - ListTraverse(L) 遍历
//对线性表L进行遍历,在遍历过程中对L的每个结点访问一次
线性表的顺序表
顺序表是在计算机内存中以数组的形式保存的线性表,采用顺序存储结构的线性表通常称为顺序表。顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中。
- InitList(&L) (Initialization List)初始
-
线性表的顺序存储
线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系。
逻辑位序和物理位序相差1
线性表顺序存储结构占用一片连续的存储结构。知道某个元素的存储位置就可以知道其他元素的存储位置,所以线性表的顺序存储结构是一种随机存取的存储结构。
假设线性表的每个元素占 l 个存储单元,则第 l+1个数据元素的存储位置和第i个数据元素的存储位置之间满足关系: LOC(ai+1)= LOC(ai)+ l
由此,所有数据元素的存储位置均可由第一个数据元素的存储位置得到:
LOC(ai+1)= LOC(ai)+(i-1) * l图示更加直白:
-
顺序表(元素)
与一维数组的区别:
C语言中:
数组静态内存分配
#define LIST_INIT_SIZE 100 //线性表存储空间的初始分配量
typedef struct
{
ElemType elem[LIST_INIT_SIZE];
int length; //当前长度
}SqList; //顺序表类型
数组动态内存分配
#define MAXSIZE 100 //线性表存储空间的初始分配量
typedef struct
{
ElemType *elem; //存储空间的基地址
int length; //当前长度
}SqList; //顺序表类型
//函数结果状态代码
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
//Status 是函数的类型,其值是函数结果状态代码
typedef int Status;
typedef char Elemtype;
1.初始化
Status InitList(SqList &L) //构造一个空的顺序表L
{
L.elem = new ElemType[MAXSIZE] //为顺序表分配空间
if(!L.elem) exit(OVERFLOW) //存储分配失败
L.length=0; //空表长度为0
return OK;
}
动态分配线性表的存储区域可以更有效地利用系统的资源,当不需要该线性表时,可以使用销毁操作及时释放占用的存储空间。
销毁线性表L
void DestroyList(SqList &L){
if(L.elem)delete L.elem; //释放存储空间
}
清空线性表L
void ClearList(SqList &L){
L.length=0; //将线性表的长度设置为0
}
求线性表L的长度
int GetLength(SqList L){
return (L.length);
}
判断线性表L是否为空
int IsEmpty(SqList L){
if (L.length == 0) return 1;
else return 0;
}
2.取值
Status GetElem(SqList L ,int i ,ElemType &e)
{
if(i<1 || i>L.length) return ERROR; //判断i值是否合理
e=L.elem[i-1]; //elem[i-1]存储第i个数据元素
return OK;
}
顺序表取值算法的时间复杂度为O(1)
3.查找
- 从第一个元素起,依次和 e 相比较,若找到与 e 相等的元素L.elem[i],则查找成功,返回该元素的序号i+1
- 若查遍整个顺序表都没有找到,则查找失败,返回0;
int LocateElem(SqList L,ElemType e) //在线性表L中查找值为e的数据元素,返回其序号(是第几个元素)
{
for(i=0 ;i<L.length ;i++)
if(L.elem[i] == e) return i+1; //查找成功,返回序号i+1
return 0; //查找失败,返回0
}
平均次数ASL=EPC = (n+1)/2
顺序表按值查找算法的平均时间复杂度为O(n)
4.插入
Status ListInsert(SqList &L,int i,ElemType 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; //将新元素 e 放入第 i 个位置
++L.length; //表长加1
return OK;
}
平均次数ASL=n/2
顺序表插入算法的平均时间复杂度为O(n)
5.删除
Status ListDelete(SqList &L,int 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; //表长减1
return OK;
}
平均次数ASL= (n-1)/2
顺序表删除算法的平均时间复杂度为O(n)
-
顺序表优缺点
-
优点:
-
存储密度大(结点本身所占存储量/结点结构所占存储量)
-
可以随机存取表中任意元素
-
-
缺点: 为了克服这一缺点,采用链表
-
在插入、删除某一元素时,需要移动大量元素
-
浪费存储空间
-
属于静态存储形式,数据元素的个数不能自由扩充
-
-
声明:此博客只是作者为了熟悉知识点而写的笔记
借鉴:《数据结构》(C语言版)(第2版)严蔚敏