数据结构学习笔记——线性表之顺序表

线性表的定义:
具有相同性质元素的一个有限序列。
序列中所含元素个数为线性表的长度。
线性表中每个元素由逻辑序号唯一确定,逻辑序号是由1开始的,而物理序号(我们说的数组的下标)是从0开始的。
序列中的第一元素叫做表头元素,最后一个元素叫做表尾元素。

线性表的抽象数据类型描述:

ADT List
{ 数据对象:
		D = {ai | 1<=i<=n, ai为ElemType对象} //ElemType是自定义类型的标识符,
		//在后面的讨论中,ai是结构体类型的。
  数据关系:
		R = {<ai,a+1> | ai、ai+1∈D,i= 1,...,n-1}
  基本运算:
  	    IniList(&L): //初始化线性表,构造一个空的线性表L。
  	    DestroyList(&L): //销毁线性表,释放线性表L所占用的空间。
  	    ListEmpty(L)//判断线性表是否为空表,若为空表范围真,否则返回假。
  	    ListLength(L): //求线性表的长度,返回L中元素的个数。
  	    DispList(L): //输出线性表,当线性表不为空时顺序显示L中各结点的值域。
  	    GetElem(L,i,&e): //求线性表中某个数据元素值,用e返回L中第i(1<=i<=n)个元素
  	    //的值。
  	    LocateElem(L,e): //按元素值查找,返回L中第一个值域与e相等的元素的序号,
  	    //若这样的元素不存在,则返回值为0;
  	    ListInsert(&L,i,e): //插入数据元素,
  	    //在L的第i(1<=i<=n)个位置插入一个新的元素e,L的长度增加1.
  	    ListDelete(&L,i,&e): //删除数据元素,删除L的第i(1<=i<=n)个元素,并用e返回
  	    //其值,L的长度减1.
}

线性表的存储结构:
线性表用顺序表来存储在计算机中。
顺序表:把线性表中的所有元素按照其逻辑关系依次存储到从计算机存储器中指定存储位置开始的一块连续的空间里。

如上图所示:由于线性表中逻辑上相邻的两个元素在顺序表中也相邻,这种映射称作直接映射。

顺序表的示意图:
每个元素是一个结构体,所以data数组是一个结构体数组,即元素是结构体的数组,常被用来存储具有相同性质的数据元素。首个元素的地址假设是loc(A),他在物理下标为0的位置上,那么第二个元素的地址是loc(A) + sizeof(a1),他在物理下标为1的位置。(所以小结,扯远一下,两个相邻的元素的地址不一定相差1.)第i个元素的地址是loc(A) + (i-1) * sizeof(a1),它在物理下标为i-1的位置。最后一个元素的地址是loc(A) + (n-1) * sizeof(a1),它在物理下标为n-1的位置。
在这里插入图片描述
之所以在最后一个元素后面还有,是因为这个顺序表是已经固定大小了的了,就算没有放元素,系统还是会按照元素的结构划分空间给这个顺序表变量。

在声明线性表的顺序存储结构类型时,定义一个data数组来存储线性表中的所有元素(一个元素可能有多个数据项),定义一个整型变量length来存储线性表的实际长度,并采用结构体类型SqList表示如下:

typedef struct
{	ElemType data[Maxsize];  //存放线性表中的元素,ElType
	int length; //存放线性表的长度
} SqList; //顺序表类型。

顺序表基本运算的实现:
访问顺序表成员的方法:
(1)通过指针访问:
Sqlist *L; //定义一个Sqlist结构体类型的指针,它专门指向Sqlist结构体。
L = (Sqlist *)malloc(sizeof(Sqlist));
L —> length;
(2)通过变量名访问:
Sqlist Q; //定义一个Sqlist结构体类型的变量
Q.length;

基本运算:
1.建立顺序表

void CreateList (SqList *&L,ElemType a[],int n) //ElemType a[]的意思是你要给函数一个ElemType类型的指针
{	
	int i = 0, k = 0;
	L = (SqList *)malloc(sizeof(SqList)); //为L分配一个指向一个空间大小为
	//sizeof(SqList)的空间的指针。
	while (i<n)
	{	
		L ->data[k] = a[i]; //把元素a[i]放入到L中
		k++;i++;
	}
	L -> length = k;
}

2.初始化顺序表:
该函数的功能是构造一个空的线性表L,实际上只需分配线性表的存储空间并将length域设置为0即可。

void InitList(SqList *&L)
{
	L = (SqList *)malloc(sizeof(SqList));
	L ->length = 0;
}

3.销毁线性表

void Destroy(SqList *&L) //&,引用符号又可以理解为使赋值者指向被赋值者所指向的空间
{
	free(L);
}

4.判断线性表是否为空集:
该运算返回一个布尔值表示L为空表。若L为空表,则返回true,否则返回false。

bool ListEmpty(SqList *L)
{
	return (0 == L -> length);
}

5.求线性表的长度:
返回线性表的length的值即可

int ListLength(SqList *L)
{
	return (L -> length);

6.输出线性表:
该运算依次显示L中个元素的值。

void DispList(SqList *L)
{
	for (int i = 0; i <n; i ++)
	{
		printf("%d", L ->data[i]);
	}
	printf("\n");
}

7求线性表中的某个数据元素值:
该运算引用形参e返回L中的第i个元素的值。

bool GetElem(SqList *L, i, &e)
{
	if (i < 1 || i > L -> length)
	 return false;
	else
	{
		e = L -> SqList.data[i-1]
		return true;
	}
}

8.按元素查找:
该运算顺序查找第一个值域与e相同的元素的逻辑序号,若这样的元素不存在,那么返回0.

int LocateElem(SqList *L, ElemType e)
{
	int i = 0;
	while (i < L.length && L.data[i] != e)
		i++;
	if (i == L.length) return 0;
	else return i+1;
}

9.插入数据元素
该运算在顺序表L的第i个位置上插入新元素e。如果i值不正确,返回false;否则将顺序表原来的第i个元素及以后的元素均后移一个位置,并从最后的一个元素开始移动。如下列代码所示,腾出一个位置插入新元素,最后顺序表的长度增1并返回ture。

bool ListInsert(Sqlist *&L,int i,ElemType e)
{
	int j;
	if (i < 1 || i > L -> length + 1 || L -> length == Maxsize)
	//第二个条件是指i,即插入的位置,不能大于最后一个元素的下一个位置。
	//第三个条件是指顺序表的数据域的元素个数会已经达到最大值,此时无法插入
		return false;
	i --;	//将顺序表逻辑序号转化为物理序号
	for (j = L -> length; j > i; j--)
	{
		L -> data[j] = L ->data[j -1] 	//把后面的元素往后挪一个位置。
	}
	L ->data[i] = e;
	L ->length ++;
	return ture;
}

移动元素的次数:
若插入位置为第一个元素,那么所有元素都要往后移,移动次数为n
如果放到最后一个位置之后的一个位置,那么所有元素都不需要移动,移动次数为0
插入到第二个元素,an 到 a2都要往后移,移动(n-2+1 = n-1)次
第三个元素,an-a3,移动(n-3 + 1)次
所以如果插入到第i个元素,那么需要移动的元素就是原先在这个位置的元素ai和这个元素之后的所有元素ai+1,ai+2,…,an,一共需要移动(n - i + 1 )次。
在这里插入图片描述

也就是说移动次数与插入位置(逻辑序号)的关系为:M = -i + n + 1,函数图像如下:
在这里插入图片描述

最大值是n,最小值是0,函数又是线性的,所以平均移动次数是n/2。
10.删除数据元素
该运算删除顺序表L的第i个元素,如果i值不正确,返回false;否则将线性表第i个元素以后的元素均向前移动一个位置,并从ai+1开始移动,如图所示,这样覆盖了原来的第i个元素,达到了删除该元素的目的,最后顺序表的长度减1并返回true。
在这里插入图片描述

bool ListDelete(SqList *&L, int i, ElemType &e)
{
	int j;
	if (i < 1 || i > L -> length )
	{
		return false;
	}
	i --;
	e = L -> data[i];
	for (j = i;j <= L ->length -1;j ++)
	{
		L ->data[j] = L ->data[j+1];
	}
	return true;
}

移动操作(注意不是元素)的次数:
若删除的元素为第一个元素,那么a2 - an 都要往前移动,最后一个元素移动之后,由于需要达到移动效果,那么就需要把an+1这个空元素的值赋给an,所以需要移动的元素应该是a2-an + an+1共n个元素,移动n次
如果想删除最后一个元素,那么只需要移动最后一个元素的后面的那个空值即可,移动次数为1
删除第二个元素,a3 到 an + an+1都要往前移,移动(n - 3 + 1 + 1 = n-1)次
第三个元素,a4-an + an+1,移动(n - 4 + 1 + 1)次
所以如果删除第i个元素,那么需要移动的元素就是这个位置的元素ai之后的所有元素ai+1,ai+2,…,an,在加一个an+1一共需要移动(n - (i+1) + 1 + 1 )次。
也就是说移动次数与插入位置(逻辑序号)的关系为:M = -i + n + 1,函数图像如下:
在这里插入图片描述

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值