文章目录
最简单的结构:线性表
线性表有两种形式:
- 顺序线性表
- 链式线性表
线性表的概念
由若干个数据组成的集合可以看做是一个线性表。线性表中的数据元素之间的关系是一对一,即除了第一个和最后一个元素外,其他元素都是首尾相接的。例如学生的学号组成的数据就是一个线性表。如下图:
202010301 | 202010302 | … | 202010329 |
---|---|---|---|
再复杂一点的情况,就是一组数据可以看做一个独立的数据元素,如图所示,将每一个学生的信息作为一个数据元素,则所有的学生的信息就组成了一个线性表(每个学生的信息为一行,看做一个数据元素,从上向下就组成一个线性表)
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);
}
我这里没有查找关键字的那个,不过道理都一样