数据结构--线性表(1)--顺序存储篇

一、线性表定义

线性表是最常见且简单的的一种线性结构。
n个(n为线性表的长度,当n=0时,线性表为空表)数据特性相同的元素构成的有限序列称为线性表。

二、线性表特点

对于非空线性表或者线性结构,其特点有
**
1).表中数据元素个数有限,数据类型同类,有先后次序。
2).表中都是数据元素,每个元素都是单个元素。
3). 除第一个数据元素外,其余元素都有前驱
4). 除最后一个数据元素外,其余元素都有后继
(前驱和后继:比如排队,在你前面的就可以称为前驱,在你后面的,称为后继)**

三、基本操作

InitList(&L)初始化线性表,构造一个空线性表。

Length(L)求表的长度,返回线性表L的长度,即L中数据元素个数。

LocateElem(L,e)按值查找。在L表中查找e元素。

GetElem(L,i)按位查找。在L表中查找在第i个位置的元素。

ListInsert(&L,i,e)插入元素。在L表中的第i个位置插入e元素。

List_Delete(&L,i,&e)删除线性表中第i个元素,并把删除的元素返回给e。

PrintList(L)输出操作。按从前后顺序输出L表中的元素值。

Empty(L)判空操作。若L为空表,则返回ture.否则返回false。

DestroyList(&L)销毁表操作。销毁线性表病,并释放表占用的空间。

四、顺序存储线性表

1.顺序表的定义

线性表的顺序存储又称顺序表,用一组地址连续存储单元依次存储线性表中数据元素。
存储结构:逻辑上相邻的两个元素在物理位置上也相同。【我的理解:在一个记录中(比如一个表中,存有有不同学生信息,含有姓名,身份证号等信息,这个称为记录),如果这个表按照姓名英文大小写排序下去的,那么存在线性表中,会安排相邻地址存储这些数据】

2.顺序表特点

a. 顺序表可以随机访问,并且在时间O(1)内找到指定的元素。
b. 顺序表的存储密度高,每个节点只存储数据元素。
c. 顺序表逻辑上相邻的元素,物理上也相邻,所以插入和删除需要移动大量元素。
d. 扩展容量不方便。

3.静态顺序存储定义线性表

1).静态顺序存储定义线性表的思想

使用静态数组实现。
其实就是存储在数组中。
大小一旦确定就无法改变。

2).静态顺序存储定义线性表的结构体

#define max_size 50       //定义数组最大存储容量
typedef struct{           //建立结构体--静态顺序存储的线性表 
   ElemType elem[max_size];    //定义一个数组存储线性表数据
   int lenght;         //线性表当前长度
}SqList; 

对 ElemType理解:
ElemType是数据结构的书上为了说明问题而用的一个词。它是element type(“元素的类型”)的简化体。 因为数据结构是讨论抽象的数据结构和算法的,一种结构中元素的类型不一定是整型、字符型、浮点型或者用户自定义类型,为了不重复说明,使用过程中用“elemtype”代表所有可能的数据类型,简单明了的概括了整体。在算法中,除特别说明外,规定ElemType的默认是int型。

理解来自链接中的文章。
https://blog.csdn.net/yatum_2014/article/details/85009062?utm_source=app&app_version=5.4.0&code=app_1562916241&uLinkId=usr1mkqgl919blen

3).静态定义线性表的基本操作

代码中 &L 和 L 的理解:当我们要修改表中值时,就要带地址符号,实际使用指针也是一样的。【我的理解:在函数模块中,执行过程是临时存储的,执行完函数之后,该函数模块就自动释放空间,数据就会被清除,如果只是传入了数据,对数据进行改动,等操作结束后,原本的数据是不会被改动的。相反,传入带地址的数据,对数据进行改动,执行结束后,数据才会得到改动,因为函数中对数据的改动是改动地址中的数据。总结:要对数据进行改动就必须传入数据地址】
InitList(&L)初始化线性表,构造一个空线性表。

bool InitList(SqList &L){            //构建一个空线性表 
	L.lenght = 0;                       //空表长度为0 
	return 1;
} 

LocateElem(L,e)按值查找。在L表中查找e元素。
思想:从表头开始依次往后查询元素。

int LocateElem(SqList L,ElemType e){
int i;   //定义循环变量
for(i = 0;i < L.length;i++)//从表头开始查找
if(L.elem[i] == e)    //判断是否找到
return i+1;   //查找成功返回其位序i+1
return 0;//循环结束表示查找失败
}

算法分析:
最好的情况:元素在表头,时间复杂度为O(1)。
最坏情况下:元素在表尾,需要比较n次,时间复杂度为O(n)。
平均情况:假设pi是查找元素在i个位置上的概率,则在长度为n的线性表中查找值为e的元素所需要比较的平均次数为:
在这里插入图片描述
线性表按值查找算法平均时间复杂度为O(n)。

ListInsert(&L,i,e)插入元素。在L表中的第i(位序)个位置插入e元素。
思想:找到第i个位置,把第i个位置以后的元素依次后移,空出位置角标在i-1处,把元素放进角标i-1的位置中。

bool ListInsert(SqList &L,int i,ElemType e)
{
	int j;
	//i的合法值为1 <= i <= L.lenght + 1
	if((i < 1) || (i > L.lenght + 1))        //判断i值是否在有效范围 
	return false;                    //i值无效返回错误 
	if(L.lenght >= max_size)         //判断表是否存满
	return false;                   //存满返回错误,不能存储
	for(j=L.lenght;j >= i;j--) //把从第i个元素以后的元素都后移一位 
	{
		L.elem[j]=L.elem[j-1];          //往后移动一个元素 
	}
	L.elem[i-1] = e;                   //把e中元素赋给空出来的位置 
	L.lenght++;//存储成功,表的长度加1
	return true;  //操作结束返回true
}

算法分析:
最好情况:在表尾直接插入,其他元素不移动,时间复杂度为O(1)。
最坏情况:在表头插入,所有元素都要移动,假设移动语句执行n次,则时间复杂度为O(n)。
平均情况:假设pi是在第 i 个位置插入元素的概率,则长度为n的线性表中插入一个节点时,所需移动节点的平均次数为:
在这里插入图片描述
所以,线性表的插入算法时间复杂度为O(n)。

List_Delete(&L,i,&e)删除线性表中第i个元素,并把删除的元素返回给e。
思想:在i值有效情况下(1<= i <=L.lenght),把elem[i-1]赋值给e再依次把第i个元素之后的元素依次往前移动一位。

bool ListDelete(SqList &L,int i,ElemType &e)
{
	int j;                                 
	if((i < 1) || (i > L.lenght))    //判断i值是否在有效范围 
	return false;                  //i值无效返回错误 
	e = L.elem[i-1];            //把角标i-1(就是第i个元素)的元素赋值给e
	for(j=i-1;j<L.lenght-1;j++) //把角标i-1之后的元素依次向前移动一位 
	{
		L.elem[j]=L.elem[j+1];
	}
	return true;  //操作结束返回true
 } 

算法分析:
最好情况:删除表尾,其他元素不移动,时间复杂度为O(1)。
最坏情况:删除表头元素,所有元素都要移动,假设移动语句执行n次,则时间复杂度为O(n)。
平均情况:假设pi是在第 i 个位置删除元素的概率,则长度为n的线性表中插入一个节点时,所需移动节点的平均次数为:
在这里插入图片描述
所以,线性表的删除算法时间复杂度为O(n)。

Empty(L)判空操作。若L为空表,则返回ture.否则返回false。
查看表长度即可。

bool Empty(L)
{
if(L.length == 0)
return 0;//返回0代表为空表
else
return 1;//返回1代表为非空表
}

4.动态顺序存储定义线性表

1).动态顺序存储定义线性表的思想

数组动态分配,利用malloc函数动态分配空间。使用realloc函数扩展空间,使用free函数释放空间。

2).动态顺序存储定义线性表

typedef struct{                //建立结构体--动态顺序存储的线性表 
    ElemType *elem;            //定义指针
	int lenght;             //当前长度 
	int listsize;              //当前分配的存储容量 
}SqList; 

lenght_DT 变量是为了方便查看存了多少个数据元素。
listsize 变量是查看当前可以存储多少数据元素。

3).动态顺序存储定义线性表的基本操作

InitList(&L)初始化线性表,构造一个空线性表。

#define OVERFLOW 0
#define LIST_INIT_SIZE   100          //线性表存储空间的初始分配量 
bool InitList(SqList &L){             //构建一个空线性表 
    L.elem = (ElemType *) malloc(LIST_INIT_SIZE * sizeof(ElemType)); 
	if(!L.elem) exit(OVERFLOW);            //存储分配失败 
	L.lenght = 0;                          //空表长度为0 
	L.listsize = LIST_INIT_SIZE;           //初始存储容量 
	return 1;
} 

下面链接是帮助malloc函数的理解:
https://blog.csdn.net/Jack11998/article/details/107341507?utm_source=app&app_version=5.4.0&code=app_1562916241&uLinkId=usr1mkqgl919blen
exit函数的理解:
函数名: exit()
所在头文件:stdlib.h
功 能: 关闭所有文件,终止正在执行的进程。
exit(1)表示异常退出.这个1是返回给操作系统的。
exit(x)(x不为0)都表示异常退出
exit(0)表示正常退出。

理解来自链接中的文章。
https://blog.csdn.net/Rex_WUST/article/details/88372481?utm_source=app&app_version=5.4.0&code=app_1562916241&uLinkId=usr1mkqgl919blen
LocateElem(L,e)按值查找。在L表中查找e元素。

int LocateElem(SqList L,ElemType e)
{
 ElemType *p,*q;             //定义两个指针变量
 int i=0; 
 q = L.elem + L.lenght-1;    //(l.elme 是首地址)使指针q指向表尾
 for(p = L.elem;p <= q;p++) //从表头开始查找
 {
 	i++;
 	if(*p == e)
 	return i;   //查到元素,返回元素位序 
 } 
 return 0;      //没查到,返回0 
}

【动态数组的查找元素代码,也可以使用静态数组值的查找的代码,这里是我自己写的,亲测,无误】

ListInsert(&L,i,e)插入元素。在L表中的第i个位置插入e元素。

bool ListInsert(SqList &L,int i,ElemType e)
{
	ElemType *p,*q;
	ElemType *newbase;//定义一个 
	//i的合法值为1 <= i <= L.lenght + 1
	if(i < 1 || i > L.lenght + 1)        //判断i值是否在有效范围 
	return false;                      //i值无效返回错误 
	if(L.lenght >= L.listsize)         //查看表是否存满 
	{
	 newbase = (ElemType *) realloc(L.elem,(L.listsize + LISTINCREMENT)* sizeof(ElemType)); //延伸存储空间 
	 if(!newbase) exit(OVERFLOW);      //存储分配失败 
	 L.elem = newbase;                //新的基址 
	 L.listsize += LISTINCREMENT;     //增加存储容量 
}
 q = &(L.elem[i-1]);   //把要插入元素地址赋给指针q
 for(p = &(L.elem[L.lenght]);p >= q;--p)//把从表尾依次把表尾前面元素向后移动一位
 *(p + 1 )= *p;//把元素向后移动一位
 *q = e;//把要插入元素赋值到移动空出来的位置
 ++L.lenght;//表长加一
	return true;
}

realloc的理解:
void realloc(void *ptr,size_t new_size); ptr是指向原来地址的指针,这个函数用于修改一个原先已经分配内存块的大小。
使用:
1.可以使一块内存扩大或缩小(原来的内存块可以扩大缩小)
(1.)如果是扩大一个内存块,则将原来的内存块保留在他的后边新增一块内存块(但是新增的内存块并未初始化)
(2.)如果是缩小一块内存块,则将该内存块的后半部分直接拿掉,剩余部分内存块及其内容保留。
2.原来的内存块无法扩大缩小 如果是着这种情况,realloc会重新开辟一个新的内存空间,并把原来的内存空间的内容拷贝到新的内存空间里。
注意:再调用完realloc后就不能使用指向就内存的指针,而是用返回的新的指针。
理解内容来自下面链接中的文章: https://blog.csdn.net/kakahaoma/article/details/87874947?utm_source=app&app_version=5.4.0&code=app_1562916241&uLinkId=usr1mkqgl919blen.
List_Delete(&L,i,&e)删除线性表中第i个元素,并把删除的元素返回给e。

bool ListDelete(SqList &L,int i,ElemType &e)
{
	ElemType *p,*q;              //定义两个指针变量 
	if(i < 1 || i > L.lenght)    //判断i值是否在有效范围 
	return false;                //i值无效返回错误 
	p = &(L.elem[i-1]);         //把要删除元素的地址赋给p 
	e = *p;                     //把p中值赋值给e 
	q = L.elem + L.lenght-1;   //(l.elme 是首地址)使指针q指向表尾 
	for(++p;p <= q;++p)      //把p地址之后的元素依次往前移动一位 
	*(p-1) = *p; 
	--L.lenght;              //表长减 1 
	return true; 

插一段根据自己需求编写调试的代码

#include <stdio.h>
#include <malloc.h> 
#include <stdlib.h>
#define LIST_INIT_SIZE   100        //线性表存储空间的初始分配量 
#define LISTINCREMENT    10         //线性表存储空间的分配增量 
#define OVERFLOW 0 
typedef int ElemType ;

typedef struct{               //建立结构体--动态顺序存储的线性表 
    ElemType *elem; 
	int lenght;               //当前长度 
	int listsize;            //当前分配的存储容量 
}SqList; 

void InitList(SqList &L){            //构建一个空线性表 
    L.elem = (ElemType *) malloc(LIST_INIT_SIZE * sizeof(ElemType)); 
	if(!L.elem) exit(OVERFLOW);         //存储分配失败 
	L.lenght = 0;                       //空表长度为0 
	L.listsize = LIST_INIT_SIZE;       //初始存储容量 
} 
bool ListInsert(SqList &L,int i,ElemType e)
{
	ElemType *p,*q;
	int k = 0;
	ElemType *newbase;//定义一个临时存储变量
	  if(i < 1 || i > L.lenght+1)        //判断i值是否在有效范围
	{
		printf("i值不合法\n"); 
		return false;
	 }  
	if(L.lenght >= L.listsize)         //查看表是否存满 
	{
	 newbase = (ElemType *) realloc(L.elem,(L.listsize + LISTINCREMENT)* sizeof(ElemType)); //延伸存储空间 
	 if(!newbase) exit(OVERFLOW);      //存储分配失败 
	 L.elem = newbase;                //新的基址 
	 L.listsize += LISTINCREMENT;     //增加存储容量 
}
 q = &(L.elem[i-1]);   //把要插入元素位置地址赋值给q
 for(p = &(L.elem[L.lenght]);p >= q;--p)//把从表尾依次把表尾前面元素向后移动一位
 *(p + 1 )= *p;
 *q = e;//把要插入数据赋值到q指向的地址中
 ++L.lenght;//表长加一
 printf("%d 插入成功\n",e); 
 return true;
}
bool ListDelete(SqList &L,int i,ElemType &e)
{
	ElemType *p,*q;         //定义两个指针变量 
	if(i < 1 || i > L.lenght)        //判断i值是否在有效范围
	{
		printf("删除失败\n");
		return false;                      //i值无效返回错误 
	 } 
	p = &(L.elem[i-1]);                //把要删除元素的地址赋给p 
	e = *p;                     //把p中值赋值给e 
	q = L.elem + L.lenght-1; //(l.elme 是首地址)使指针q指向表尾 
	for(++p;p <= q;++p) //把p地址之后的元素依次往前移动一位 
	*(p-1) = *p; 
	--L.lenght;      //表长减 1 
	printf("删除成功\n");
	return true; 
}
//查找元素是否在线性表中 
int LocateElem(SqList L,ElemType e)
{
 ElemType *p,*q;             //定义两个指针变量
 int i=0; 
 q = L.elem + L.lenght-1;  //(l.elme 是首地址)使指针q指向表尾
 for(p = L.elem;p <= q;p++) 
 {
 	i++;
 	if(*p == e)
 	return i;   //查到元素,返回元素位序 
 } 
 return 0;      //没查到,返回0 
}
void Pint(SqList L)//遍历线性表函数
{
	int i;
	printf("遍历线性表\n");
	for(i = 0;i < L.lenght;i++)
	{
		printf("%d\n",L.elem[i]);
	}
}
int main()
{
	SqList W; 
	InitList(W);
	int i,n,k; //i循环条件的变量,n插入数据的个数,K临时存储插入的数据
	int p,a; //p为删除元素的位序,a为第p位地数据
	int g,z;// g为查找的数据,z为查到数据后返回数据的位序。 
	printf("请输入要存入的元素个数:\n");
	scanf("%d",&n);
	for(i = 1;i <= n;i++)
	{
		printf("请输入插入元素:\n");
		scanf("%d",&k);
		ListInsert(W,i,k);        //调用插入函数 
	 } 
	printf("请输入要删除第几位数据:\n"); 
	scanf("%d",&p);
	ListDelete(W,p,a);           //调用删除函数 
	printf("删除的元素为 %d \n",a);
	Pint(W);                     //调用遍历函数 
	printf("输入想要查找的数据:\n");
	scanf("%d",&g);
	z = LocateElem(W,g);         //调用查找函数,查找
	if(z == 0)                   //判断位序是否合法 
	  printf("表中不存在该元素\n"); 
	else 
	  printf("查找数据在第%d位",z);
}

脑图脑图

在这里插入图片描述

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

An安之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值