数据结构与算法--线性表的顺序存储

线性表的定义与逻辑结构

  • 线性表由n个相同属性的数据元素组成,可以为空表或表示为 ( A = (a_1, a_2, …, a_i, …, a_n) )。
  • 线性表的逻辑特征包括:
    • 有且仅有一个表头元素 ( a_1 ),无前驱。
    • 有且仅有一个表尾元素 ( a_n ),无后继。
    • 表头元素和表尾元素之外的其他元素都有一个前驱和一个后继。
    • 线性表的长度为n,当n=0时,称为空表。
    • 一般形式:
      A=(a1,a2,…,ai,…,an)
  • 线性表数据结构定义
    Linear_list=(D,R)
    D={ai| ai A, i=1,2,…,n; n0}
    R={< ai , ai+1 > | 1 i n-1}

其中,关系的定义< ai , ai+1 >:ai , ai+1 必定相邻;ai 必定在ai+1在之前(如果 i+1存在);ai+1必定在ai在之后

线性表举例

1、n维向量(x1, x2,…, xn)是一个长度为n的线性表
2、英文小写字母表(a,b,c,…,z)是一个长度为26的线性表
3、一年中的四个季节(春,夏,秋,冬)是一个长度为4的线性表
4、矩阵是一个比较复杂的线性表
5、学生情况登记表是一个复杂的线性表

  • 由若干数据项组成的数据元素称为记录
  • 由多个记录构成的线性表又称为文件(file)
  • 在这里插入图片描述

线性表的抽象数据类型(ADT)

  • 数据元素:( D = { a_i | A, i=1,2,…,n, n\geq0 } )
  • 数据关系:( R_1 = { \langle a_{i-1}, a_i \rangle | a_{i-1}, a_i \in D, i=2,…,n } )
  • 操作包括:
    • 初始化(InitList):初始化线性表
    • 求表长(ListLength):求线性表中数据元素个数
    • 判空表(ListEmpty):判定线性表是否为空
    • 插入(ListInsert):在指定位置插入数据元素
    • 删除(ListDelete):删除指定位置上的数据元素
    • 取值(GetElem):获取指定位置数据元素的值

线性表的存储结构

  • 线性表可以通过顺序存储和链式存储两种方式存储在计算机内存中。
    • 顺序存储使用数组实现,称为顺序表。

    • 在这里插入图片描述

    • 链式存储使用指针实现,称为链表。

    • 在这里插入图片描述

顺序表的定义

  • 用一组地址连续的存储单元依次存储线性表中每个数据元素,这种存储结构称为线性表的顺序存储结构,用这种结构表示的线性表称为顺序表。
  • 顺序表的特点:用数据元素在计算机内物理位置相邻来表示线性表中数据元素之间的逻辑关系。
  • 线性表中第i个数据元素ai的存储位置(物理地址):
    LOC(ai)=LOC(a1)+(i-1)×m
    • LOC(a1)为线性表的首地址或基地址;m为线性表中的每个数据元素占用的存储单元数。
  • 线性表的顺序存储结构为随机存储结构。
  • 顺序存储结构的实现:
  • 在这里插入图片描述

顺序表的定义与内存视图

  • 顺序表使用连续的存储单元存储线性表的每个数据元素。
  • 顺序表的特点是物理位置相邻表示逻辑关系。
  • 顺序表的内存视图包括数组长度、线性表长度、数组首地址等。

顺序表的C++语言描述

#define MaxSize 100
typedef int ElemType; // 声明顺序表的元素类型
typedef struct {
    ElemType data[MaxSize]; // 存放顺序表数据
    int length; // 保存线性表数据个数
} SqList; // SqList 为用户定义的顺序表类型

MaxSize表示顺序表中最多元素个数。
ElemType是一个抽象类型。在实现时,要定义为具体的数据类型。 如:typedef char ElemType;
Length表示线性表的当前表长,非负值且<=MaxSize;

  • SqList my_list; //定义顺序表类型的变量my_list
    • SqList //线性表数据类型
    • my_list SqList类型的变量my_list 拥有两个数据域:
      数组:data 存放线性表数据
      变量:length 保存线性表数据个数
顺序表程序实现方法
  • 声明线性表的数据类型
    • 一般在头文件中声明(该头文件中还应包括操作接口的声明)
    • 告诉编译器该数据类型占内存空间的大小
  • 定义线性表类型的变量
    • 一般在函数中或实现代码中定义
    • 告诉编译器为变量分配内存空间
  • 操作线性表类型变量
    • 操作变量中保存的数据

顺序表的基本操作

注意在实现每一个操作时都要确保当前存储结构下的线性表其逻辑结构不变。

  • 初始化顺序表:InitList(SqList &L)
int InitList(SqList &L)    //考虑为何用引用作参数
{                       //L为指向顺序表类型的引用
   L.length=0;
   return 1;
}
本算法的时间复杂度为O(1)。
在函数中,需要改变顺序表的length域的值,因此参数L设计为一个引用或指针(需要实参的地址)。
  • 求顺序表中当前元素个数:ListLength(SqList L)
int ListLength(SqList L)
{
	return L.length;
}
本算法的时间复杂度为:O(1)
  • 判断顺序表是否为空:ListEmpty(SqList L)
int ListEmpty(SqList L)
{
	if(L.length<=0)	return 1;
	else	return 0;
} 
本算法的时间复杂度为:O(1)
  • 顺序表的插入算法:ListInsert(SqList &L, int pos, ElemType item)

  • 在这里插入图片描述
    插入item后,ai-1和ai之间的逻辑关系< ai-1, ai >变成了<ai-1, item>和<item, ai>,必须移动数据元素才能体现这种逻辑关系的变化。
    在这里插入图片描述

  • 插入操作步骤:

    • 1.判断所给顺序表是否已满,若满则产生上溢出错误。
    • 2.检查插入位置是否合法,即“1≤pos≤length+1”条件是否满足。
    • 3.将顺序表最后一个(第length个)元素到第pos个元素之间的所有元素依次向后移动一个位置,为新元素空出插入位置。
    • 4.将新元素插入于空出的位置上。
    • 5.顺序表长度增一。
    • 注意,这里的pos为逻辑位置,即从1开始计数。
int ListInsert(SqList &L, int pos, ElemType item)
{//pos为插入的(逻辑)位置,item为待插入的数据元素
	int i;
	if(L.length>=MaxSize){                  //判表满
		cout<<“顺序表满,无法插入!”<<endl;
            return 0;}
	if(pos<=0 || pos>L.length+1){           //判位置
		cout<<“插入位置无效!”<<endl;
		return 0;}
	for(i=L.length-1;  i>=pos-1;  i--)     //向后移动元素
		L.data[i+1]=L.data[i];
	L.data[pos-1]=item;		            //插入	
  L.length++;			            //表长增一
	return 1; 
}       
  • 顺序表的删除算法:ListDelete(SqList &L, int pos, ElemType *item)

  • 在这里插入图片描述
    删除ai后,ai-1、ai和ai+1之间的逻辑关系< ai-1, ai >, < ai, ai+1 >变成了<ai-1, ai+1>,为了反映这种逻辑关系的变化,必须移动表中的数据元素。
    在这里插入图片描述

  • 删除操作步骤:

    • 1.判断所给顺序表是否为空,若空则产生下溢出错误。
    • 2.检查删除位置是否合法,即“1≤pos≤length”条件是否满足。
    • 3.将顺序表的第pos+1个元素到第length个元素之间的所有元素依次向前移动一个位置。
    • 4.顺序表长度减一。
    • 注意,这里的pos为逻辑位置,即从1开始计数。
int ListDelete(SqList &L, int pos, ElemType *item)
{    //pos为删除的(逻辑)位置,用item返回被删元素
	int i;
	if(ListEmpty(L)){                     //判表空
		cout<<“顺序表为空表,无法删除!”<<endl;
            return 0; }
	if(pos<1 || pos>L.length){       //判位置
		cout<<“删除位置无效!”<<endl;
		return 0; }
	*item=L.data[pos-1];           //保存被删元素的值,备用
	for(i=pos;i<L.length;i++)      //向前移动元素	
          L.data[i-1]=L.data[i];
	L.length--;		           //表长减一
	return 1; 
}顺序表的删除
  • 获取顺序表中指定位置上的数据元素:GetElem(SqList L, int pos, ElemType *item)
int GetElem(SqList L, int pos, ElemType *item)
{ //pos为指定位置,item用于返回找到的数据元素if(ListEmpty(L))	return 0;
	if(pos<=0 || pos>L.length){
		cout<<“位置无效”<<endl;
		return 0;}
	*item=L.data[pos-1];
	return 1;
} 
顺序表可以随机获取任何位置上的元素。
其时间复杂度为常量O(1),即与顺序表的长度无关。
。
  • 查找指定元素在顺序表中的位置:Find(SqList L, ElemType item)
判断是否为空表。
从第一个元素起,依次进行比较。
当找到与之相等的元素时,返回该元素在顺序表中的位序。若找遍整个顺序表都没有找到,则返回0,表示查找失败。
int Find(SqList L, ElemType item)
{//item为待查找的数据元素
	int pos=0;
	if(ListEmpty(L)){
		cout<<“顺序表为空表,无法查找!”<<endl;
		return 0;}
	while(pos<L.length && L.data[pos]!=item) pos++;
	if(pos<L.length)	return pos+1;
	else	                    return 0;
}   
本算法的时间复杂度为:O(n)
  • 遍历顺序表:TraverseList(SqList L)
int TraverseList(SqList  L)
{
     int i;
	for(i=0;i<L.length;i++) 
           cout<<L.data[i]<<“  ”;
	cout<<endl;
     return 1;
} 
本算法的时间复杂度为O(n)

顺序存储结构的优缺点

  • 优点:
    • 逻辑相邻,物理相邻。
    • 可随机存取任一元素。
    • 存储空间使用紧凑。
  • 缺点:
    • 插入、删除操作需要移动大量的元素。
    • 预先分配空间需按最大空间分配,利用不充分。
    • 表容量难以扩充。

需要思考的问题

  • 如何表示多项式?
    • 表示系数。
    • 表示系数的阶次。
  • 如何保存?
    • 数据保存。
    • 关系保存。
  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值