线性表概述及其顺序存储结构代码实现

一、线性表的定义

  线性表是具有相同数据类型的n(n>=0)个数据元素的有限序列,其中n为表长,当 n 等于 0 时线性表是一个空表。若用L命名线性表,则其一般表示为
          L= (a1, a2, a1, … ,ai+l, … ,an)
在这里插入图片描述
式中,a1是唯一的 “第一个” 数据元素,又称表头元素; an是唯一的 “最后一个” 数据元素,又称
表尾元素。除第一个元素外,每个元素有且仅有一个直接前驱。除最后-一个元素外, 每个元素有且仅有一个直接后继

由以上定义可以得出线性表的特点如下,表中元素:

  • 个数有限
  • 具有逻辑上的顺序性,有其先后次序
  • 都是数据元素,每个元素都是单个元素。
  • 的数据类型都相同,这意味着每个元素占有相同大小的存储空间
  • 具有抽象性,即仅讨论元素间的逻辑关系,而不考虑元素究竞表示什么内容。

注意: 线性表是一种逻辑结构,表示元素之间一对一的相邻关系。顺序表和链表是指存储结构,两者属于不同层面的概念。

二、线性表的基本操作

一个数据结构的基本操作是指其最核心、最基本的操作。其他较复杂的操作可通过调用其基
本操作来实现。线性表的主要操作如下:

  • InitList(&L):初始化表。构造一个空的线性表。
  • Length(L):求表长。返回线性表L的长度,即 L 中数据元素的个数。
  • LocateElem(L,e):按值查找操作。在表 L 中查找具有给定关键字值的元素,并返回下标。
  • GetElem(L,i):按位查找操作。获取表 L 中第 i 个位置的元素的值。
  • ListInsert(&L,i,e):插入操作。在表 L中的第 i 个位置上插入指定元素e。
  • ListDelete(&L,i,&e):删除操作。删除表 L 中第 i 个位置的元素,并用 e 返回删除元素的值。
  • PrintList(L):输出操作。按前后顺序输出线性表 L 的所有元素值。
  • Empty(L):判空操作。若L为空表,则返回true,否则返回false。
  • DestroyList(&L):销毁操作。销毁线性表,并释放线性表L所占用的内存空间。

三、顺序存储结构表示

3.1 顺序表的定义

线性表的顺序存储又称顺序表。它是用一组地址连续的存储单元依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻。
在这里插入图片描述
假定线性表的元素类型为ElemType,则线性表的顺序存储类型描述为:

typedef struct Sqlist
{
	ElemType data[MaxSize];// 数组,存储数据元素
	int length;				// 线性表当前长度 
}SqList;

  一维数组可以是静态分配的,也可以是动态分配的。在静态分配时,由于数组的大小和空间事先已经固定,一旦空间占满,再加入新的数据将会产生溢出,进而导致程序崩溃。
  而在动态分配时,存储数组的空间是在程序执行过程中通过动态存储分配语句分配的,一旦数据空间占满,就另外开辟一块更大的存储空间,用以替换原来的存储空间,从而达到扩充存储数组空间的目的,而不需要为线性表一次性地划分所有空间。

#define InitSize 100	//表长度的初始定义
typedef struct Sqlist
{
	ElemType *data;		// 指示动态分配数组的指针
	int length;			// 线性表当前长度 
	int MaxSizel		//数组的最大容量
}SqList;

初始化动态分配语句为:

L.data= (ElemType* ) malloc (sizeof (ElemType) *InitSize) ;
顺序表的特点
  • 顺序表最主要的特点是随机访问,即通过首地址和元素序号可在时间0(1)内找到指定的元素。
  • 顺序表的存储密度高,每个结点只存储数据元素。
  • 顺序表逻辑上相邻的元素物理上也相邻,所以插入和删除操作需要移动大量元素。

3.2 顺序表操作的基本实现

  线性表的基本操作还是很多的,这里只介绍两种稍微难理解的两种方法:插入操作和删除操作。

3.2.1 顺序表的插入操作

  在顺序表 L 的第 i (1<=i<=L.1ength+1) 个位置插入新元素e。若 i 的输入不合法,则返回 false ,表示插入失败。否则,将顺序表的第 i 个元素及其后的所有元素右移一个位置,腾出一个空位置插入新元素 e,顺序表长度增加 1 ,插入成功,返回true。插入操作过程如下图所示:
在这里插入图片描述
插入操作的思路

  • 如果插入位置不合理,抛出异常; .
  • 如果线性表长度大于等于数组长度,则抛出异常或动态增加容量;
  • 从最后 一个元素开始向前遍历到第 i 个位置,分别将它们都向后移动一个位置;
  • 将要插入元素填入位置 i 处;
  • 表长加1。

实现代码如下

/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L), */
/* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */
bool ListInsert(SqList *L, int i, ElemType e)
{
	if(L->length >= MaxSize)	//顺序表已满
		return false;
	if(i < 1 || i > L->length + 1)	//当i不在范围内时
		return false;
	for(int j = L->length;j >= i;j--)	//将第j个元素之后的向后移,从最后一个元素开始移动,直到第j个元素移动完成
	{
		L->data[j] = L->data[j-1];
	}
	L->data[i-1] = e;	//在位置i出放e
	L->length++;
	return true;
}

注意:区别顺序表的位序数组下标。为何判断插入位置是否合法时if语句中用 length+1,而移动元素的for语句中只用length?

接下来分析一下时间复杂度

  • 最好情况:在表尾插入(即i=n+ 1),元素后移语句将不执行,时间复杂度为O(1)。
  • 最坏情况:在表头插入(即i= 1),元素后移语句将执行n次,时间复杂度为O(n)。
  • 平均情况:假设pi (pi=1/(n+1))是在第i个位置上插入一个结点的概率,则在长度为n的线性表中插入一个结点时,所需移动结点的平均次数为
    在这里插入图片描述
    因此,线性表插入算法的平均时间复杂度为O(n)。
3.2.2 顺序表的删除操作

  删除顺序表L中第i (1<=i<=L. length)个位置的元素,若成功则返回true,并将被刪除的元素用引用变量e返回,否则返回false. 删除思路如下:
在这里插入图片描述
删除算法的思路:

  • 如果删除位置不合理,抛出异常;
  • 取出删除元素;
  • 从删除元素位置开始遍历到最后一个元素位置,分别将它们都向前移动一
    个位置;
  • 表长减1。

实现代码如下

bool ListDelete(SqList *L, int i, ElemType *e)
{
	if(L->length == 0)//线性表为空
		return 0;
	if(i < 1 || i > L->length)	//删除位置不正确
		return false;
	*e = L->data[i - 1];
	for(int j = i;j < L->length;j++)	//将删除位置后的元素前移
	{
		L->data[j - 1] = L->data[j];
	}
	L->length--;
	return true;
}

接下来分析一下时间复杂度

  • 最好情况:删除表尾元素(即i=n),无须移动元素,时间复杂度为O(1)。
  • 最坏情况:删除表头元素(即i= 1),需移动除第一个元素外的所有元素,时间复杂度为O(n)。
  • 平均情况:假设pi (pi= 1/n)是删除第i个位置上结点的概率,则在长度为n的线性表中删除一个结点时,所需移动结点的平均次数为:
    在这里插入图片描述
    因此,线性表删除算法的平均时间复杂度为O(n)。

3.3 完整代码如下

#include<stdio.h>
#include<stdlib.h>


#define MaxSize 50
typedef int ElemType;

typedef struct Sqlist
{
	ElemType data[MaxSize];// 数组,存储数据元素
	int length;				// 线性表当前长度 
}SqList;

int InitList(SqList *L);	//初始化顺序表
bool AppendList(SqList *L, ElemType e);	//在顺序表末尾插入数据
bool ListInsert(SqList *L, int i, ElemType e);	//在顺序表第i个位置插入数据	
void PrintList(SqList L);	//输出顺序表数据
int LocateElem(SqList L, ElemType e);	//按值查找,返回在顺序表中的位置
int GetElem(SqList L, int i,ElemType *e);	//按位查找操作
int ListLength(SqList L);
bool Empty(SqList L);//判断空操作
bool ListDelete(SqList *L, int i, ElemType *e);//删除操作,删除第i个位置元素,并返回删除的元素


int main()
{
	SqList L;
	ElemType e;
	int i = InitList(&L);
	bool b;
	printf("初始化List后:L.length = %d\n",L.length);
	

	AppendList(&L, 145);
	AppendList(&L, 59);
	AppendList(&L, 6);
	AppendList(&L, 26);
	AppendList(&L, 61);
	AppendList(&L, 98);
	AppendList(&L, 74);

	PrintList(L);

	printf("\n");

	b = ListInsert(&L,5,999);
	
	PrintList(L);

	i = LocateElem(L,999);
	printf("\n999元素在顺序表中第 %d 个\n",i);

	i = GetElem(L,3,&e);
	if(i == 1)
		printf("位置 3 的元素为:%d\n",e);
	else
		printf("获取无效!!");


	ListDelete(&L, 5, &e);
	printf("删除的元素为:%d\n",e);
	PrintList(L);

	return 0;
}

/* 初始化顺序线性表 */
int InitList(SqList *L)
{
	L->length = 0;
	return 1;
}


bool AppendList(SqList *L, ElemType e)
{
	if(L->length >= MaxSize)
		return false;
	L->data[L->length] = e;
	L->length++;
	return true;
}


/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L), */
/* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */
bool ListInsert(SqList *L, int i, ElemType e)
{
	if(L->length >= MaxSize)
		return false;
	if(i < 1 || i > L->length + 1)
		return false;
	for(int j = L->length;j >= i;j--)	//将第i个元素之后的向后移
	{
		L->data[j] = L->data[j-1];
	}
	L->data[i-1] = e;	//在位置i出放e
	L->length++;
	return true;
}

void PrintList(SqList L)
{
	printf("线性表元素为:");
	for(int i = 0;i < L.length;i++)
	{
		printf("%d ",L.data[i]);
	}
}

int LocateElem(SqList L, ElemType e)
{
	if(L.length == 0)//为空判断
		return 0;
	for(int i = 0;i < L.length;i++)
	{
		if(L.data[i] == e)
			return i + 1;
	}
	return 0;
}


int GetElem(SqList L, int i,ElemType *e)
{
	if(L.length == 0 || i < 1 || i > L.length + 1)
		return 0;
	*e = L.data[i-1];
	return 1;
}

/* 初始条件:顺序线性表L已存在。操作结果:返回L中数据元素个数 */
int ListLength(SqList L)
{
	return L.length;
}


bool Empty(SqList L)
{
	if(L.length == 0)
		return false;
	else
		return true;
}


bool ListDelete(SqList *L, int i, ElemType *e)
{
	if(L->length == 0)//线性表为空
		return 0;
	if(i < 1 || i > L->length)	//删除位置不正确
		return false;
	*e = L->data[i - 1];
	for(int j = i;j < L->length;j++)
	{
		L->data[j - 1] = L->data[j];
	}
	L->length--;
	return true;
}

四、顺序表存储结构的优缺点

优点

  • 无须为表示表中元素之间的逻辑关系而增加额外的存储空间
  • 可以快速地存取表中任一位置的元素

缺点

  • 插入和删除操作需要移动大量元素
  • 当线性表长度变化较大时,难以确定存储空间的容量
  • 造成存储空间的 “碎片"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值