线性表详解(附代码)

10 篇文章 0 订阅
7 篇文章 1 订阅

最简单的结构:线性表

线性表有两种形式:

  • 顺序线性表
  • 链式线性表

线性表的概念

​ 由若干个数据组成的集合可以看做是一个线性表。线性表中的数据元素之间的关系是一对一,即除了第一个和最后一个元素外,其他元素都是首尾相接的。例如学生的学号组成的数据就是一个线性表。如下图:

202010301202010302202010329

​ 再复杂一点的情况,就是一组数据可以看做一个独立的数据元素,如图所示,将每一个学生的信息作为一个数据元素,则所有的学生的信息就组成了一个线性表(每个学生的信息为一行,看做一个数据元素,从上向下就组成一个线性表)

202010401黎明18岁
202010402郭富城18岁
202010430张学友18岁
202010431刘德华18岁
202010432靓仔18岁

现在大概理解,线性表是一个线性结构,它是一个含有N个节点的有限序列。

注意:
不同的线性表的元素可以是不同的,但是对于一个线性表,各个元素必须具有相同的数据类型,即同一线性表中各数据元素具有相同的类型,每一个数据元素的长度相同。

线性表有以下的特征:

  • 有且只有一个“首元素”,它没有直接的前驱,只有一个直接后继

  • 有且是有一个“末元素”,它没有直接的后继,只有一个直接前驱

  • 其他元素均有一个直接的前驱和直接后继;

  • 元素之间为一对一的线性关系

    在计算机保存线性表时,一般可以用顺序存储结构和链式存储结构两种方法。顺序存储结构的线性表称为顺序表,链式存储就是链表。

    对于线性表,主要有以下操作:

  • 添加节点;

  • 插入节点;

  • 删除节点;

  • 查找节点

  • 遍历节点;

  • 统计节点数;

下面来看看在顺序表和链表中如何实现这些操作。

提示:
队列,栈是线性表的特殊情况。在后面也会介绍到!

操作顺序表

​ 采用顺序存储的方式的线性表称为顺序表。顺序表是线性表的一种最简单的和最常见的 方式,这种方式用一组地址连续的存储单元依次保存线性表中的数据元素。

​ 顺序结构是将表中的元素一个接一个的存入一组连续的存储单元中。由于顺序表示依次存放的,因此只要知道该顺序表的首地址即每一个数据元素所占的存储长度,就很容易计算出任何一个数据元素的位置,也可以很方便的将数据元素添加在顺序表中。

​ 要访问第i个数据元素,可以使用以下公式计算出第i个元素的存储位置L:

L=(i-1)*数据元素长度

例如:若数据元素的长度为4个字节,则第五个元素的存储位置(相对位置)为:

L=(5-1)*4;
L=16;

​ 即,保存的5个元素的地址相对于线性表的首位置偏移了16个字节,总这里开始保存第五个元素。

​ 了解顺序表存储结构以后,就可以编写代码来操作顺序表了。

​ 在编写具体的C语言代码前,首先要说一下代码组织的约定,为了让操作这些数据结构的代码具有通用性,每种数据结构使用三个文件来保存。

  • 头文件:用来保存数据结构的定义,操作函数的原型声名等。程序员可以修改其中的数据结构定义,以适应不同需求。
  • 作函数文件:用来保存操作数据结构的相关函数,这个文件的内容应该固定,程序员一般不做修改,只是直接调用而已。该文件不用包含main函数。
  • 试文件(调用函数):在文件中应包含上面的两个函数,然后编写代码实现对数据的操作。在该文件中编写main函数。
1、定义顺序表结构

​ 由于C语言中的数组也是采用顺序存储的方式,因此,可使用数组来模拟顺序表的保存形式。在顺序表中还需定义一个变量,用来保存顺序表中已经有的元素的数量。因此,可以使用以下的结构来定义顺序表结构。

#include <cstdio>
#include <cstring>
#define MAXSIZE 100				//定义顺序表的最大长度 

typedef struct 					//定义顺序表的结构 
{
	DATA ListData[MAXSIZE+1];	//保存顺序表的数组 
	int ListLen;				//顺序表已存的节点数量 
}SeqListType;

为了方便,数组序号为0 的序号不用。

这里使用了一个复杂数据类型DATA(由结构体定义的多种数据)这样可以使线性表更有实际意义。也就是你可以把DATA改成结构体,int ,double等。

2、定义顺序表的操作

​ 接着,在操作顺序表的头文件中定义操作顺序表的函数原型,具体代码如下:

void SeqListInit(SeqListType *SL);//初始化顺序表 
int SeqListLength(SeqListType *SL);//返回顺序表的元素数量 
int SeqListAdd(SeqListType *SL,DATA data);//向顺序表中添加元素 
int SeqListInsert(SeqListType *SL,int n,student data);//向顺序表中插入元素 
int SeqListDelete(SeqListType *SL,int n); //删除顺序表中的数据元素 
DATA *SeqListFindByNum(SeqListType *SL,int n); //根据序号返回元素
int SeqListFindByCont(SeqListType *SL,char *key);//根据关键字查找
int SeqListAll(SeqListType *SL);//遍历顺序表的内容 

各个函数的作用都注释了

3、初始化顺序表

接下来就需要编写顺序表的各函数了,这些函数的代码可以保存在另外一个文件中。

首先介绍初始化顺序表,要初始化顺序表,只需要设置该顺序表的元素为0即可,以后添加元素到顺序表中时从第一个位置开始。

提示:
如果顺序表原来已经有数据,也会被覆盖
void SeqListInit(SeqListType *SL)
{
	SL->Listlen	=0;
}
4、顺序表的长度

在顺序表中,字段ListLen中保存这顺序表中节点的数量,要获得顺序表的长度只需要返回该字段的值就好了

int SeqListLength(SeqListType *SL)
{
	return (SL->ListLen);
}
5、添加节点

添加节点的操作时在顺序表的最后一个位置添加节点数据。在添加节点时,首先要判断顺序表的空间是否还有,如果没有满再将数据传入到顺序表的下一个位置就好了。具体代码如下:

int SeqListAdd(SeqListType *SL,student data)
{
	if(SL->ListLen>=MAXSIZE)
	{
		printf("顺序表满了");
		return 0; 
	}
	SL->ListData[++SL->ListLen]=data;
	return 1; 
}

该函数使用了两个参数,其中SL指向顺序表的指针,而data是要添加的数据

6、插入节点

在顺序表中插入节点的操作比较麻烦,因为顺序表中的所有节点都是按序号紧邻存放的,如果要插入一个节点,则需要将插入节点的后面的数据都向后移动一位。

int SeqListInsert(SeqListType *SL,int n,student data)
{
	int i;
	if(SL->ListLen>=MAXSIZE)
	{
		printf("顺序表满了");
		return 0; 
	}
	if(n<1||n>SL->ListLen-1)
	{
		printf("插入点错误");
		return 0; 
	}
	for(i=SL->ListLen;i>=n;i--)
	SL->ListData[i+1]=SL->ListData[i];
	SL->ListData[n]=data;
	SL->ListLen++;
	return 1;
}

其实这样做的效率是有点低的

7、删除节点

与插入节点类似的,要移动大量的数据。如果删除n节点,则n+1到末尾的都要向前移动,然后长度-1。

具体代码如下:

int SeqListDelete(SeqListType *SL,int n)
{
	int i;
	if(n<1||n>SL->ListLen +1)
	{
		printf("节点错误"); 
		return 0; 
	 } 
	 for(int i=n;i<SL->ListLen;i++)
	 {
	 	SL->ListData [i]=SL->ListData [i+1];
	 }
	 SL->ListLen --;
	 return 1;
} 
8、按序号查找节点

创建好顺序表之后,最常用的就是在表中查找。代码如下:

student *SeqListFindByNum(SeqListType *SL,int n)
{
	if(n<1||n>SL->ListLen +1)
	{
		printf("节点错误");
		return NULL; 
	}
	return &(SL->ListData [n]);
}

返回序号为n的节点指针

9、按照关键字查找节点

在实际查找中,可能更多用关键字查找。例如:

在学生信息中,按学号查找也比较常见。

具体代码如下:

int SeqListFindByCont(SeqListType *SL,char *key)
{
	int i;
	for(i=1;i<SL->ListLen;i++)
	{
		if(strcmp(SL->ListData [i].key,key)==0)
		return i;
		return 0;
	}
}
10、遍历顺序表:

直接上代码:

int SeqListAll(SeqListType *SL)
{
	int i;
	for(i=1;i<=SL->ListLen ;i++)
	{
		printf("%d,%d,%f,%s",SL->ListData [i].num,SL->ListData [i].score,SL->ListData [i].GPI,SL->ListData [i].key);
	}
}

运用实例:

#include <cstdio>
#include <cstring>
#define MAXSIZE 100				//定义顺序表的最大长度 

struct student {
	int num;
	int score;
	float GPI;
};
typedef struct 					//定义顺序表的结构 
{
	student ListData[MAXSIZE+1];	//保存顺序表的数组 
	int ListLen;				//顺序表已存的节点数量 
}SeqListType;

void SeqListInit(SeqListType *SL);//初始化顺序表 
int SeqListLength(SeqListType *SL);//返回顺序表的元素数量 
int SeqListAdd(SeqListType *SL,student data);//向顺序表中添加元素 
int SeqListInsert(SeqListType *SL,int n,student data);//向顺序表中插入元素 
int SeqListDelete(SeqListType *SL,int n); //删除顺序表中的数据元素 
student *SeqListFindByNum(SeqListType *SL,int n); //根据序号返回元素
int SeqListFindByCont(SeqListType *SL,char *key);//根据关键字查找
int SeqListAll(SeqListType *SL);//遍历顺序表的内容 
void SeqListInit(SeqListType *SL)
{
	SL->ListLen=0;
}
int SeqListLength(SeqListType *SL)
{
	return (SL->ListLen);
}
int SeqListAdd(SeqListType *SL,student data)
{
	if(SL->ListLen>=MAXSIZE)
	{
		printf("顺序表满了");
		return 0; 
	}
	SL->ListData[++SL->ListLen]=data;
	return 1; 
}
int SeqListInsert(SeqListType *SL,int n,student data)
{
	int i;
	if(SL->ListLen>=MAXSIZE)
	{
		printf("顺序表满了");
		return 0; 
	}
	if(n<1||n>SL->ListLen-1)
	{
		printf("插入点错误");
		return 0; 
	}
	for(i=SL->ListLen;i>=n;i--)
	SL->ListData[i+1]=SL->ListData[i];
	SL->ListData[n]=data;
	SL->ListLen++;
	return 1;
}
int SeqListDelete(SeqListType *SL,int n)
{
	int i;
	if(n<1||n>SL->ListLen +1)
	{
		printf("节点错误"); 
		return 0; 
	 } 
	 for(int i=n;i<SL->ListLen;i++)
	 {
	 	SL->ListData [i]=SL->ListData [i+1];
	 }
	 SL->ListLen --;
	 return 1;
} 
student *SeqListFindByNum(SeqListType *SL,int n)
{
	if(n<1||n>SL->ListLen +1)
	{
		printf("节点错误");
		return NULL; 
	}
	return &(SL->ListData [n]);
}
/*int SeqListFindByCont(SeqListType *SL,char *key)
{
	int i;
	for(i=1;i<SL->ListLen;i++)
	{
		if(strcmp(SL->ListData [i].key,key)==0)
		return i;
		return 0;
	}
}*/
int SeqListAll(SeqListType *SL)
{
	int i;
	for(i=1;i<=SL->ListLen ;i++)
	{
		printf("%d,%d,%f\n",SL->ListData [i].num,SL->ListData [i].score,SL->ListData [i].GPI);
	}
}
int main()
{
	int i;
	SeqListType SL;
	student data,*datal;
	char key[15];
	
	SeqListInit(&SL);
	
	do{
		printf("输入添加节点:(学号,成绩,GPI):\n");
		fflush(stdin);//清空输入缓冲区
		scanf("%d%d%f",&data.num,&data.score,&data.GPI);
		if(data.num!=0)
		{
		if(!SeqListAdd(&SL,data))
		break;
		 } 
		else 
		break;
	}while (1);
	printf("顺序表中的节点顺序为:\n");
	SeqListAll(&SL);
	
	fflush(stdin);
	printf("要取出的节点顺序\n");
	scanf("%d",&i);
	datal=SeqListFindByNum(&SL,i);
	if(datal)
	printf("%d %d %f",datal->num ,datal->score ,datal->GPI );
	fflush(stdin); 
	
}

我这里没有查找关键字的那个,不过道理都一样

在这里插入图片描述

  • 8
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值