这是数据结构入门系列笔记的第一篇。
线性表的定义
由 n (n>=0)个数据元素(节点)a1,a2,...an组成的有序序列。
特点:
- 在非空的线性表,有且仅有一个开始节点
a1,它没有最直接前趋,有且仅有一个直接后继a2 - 有且仅有一个终端节点
an,它没有直接后继,而仅有一个直接前趋an-1 - 其余的内部节点
ai(2<=i<=n-1)都有且仅有一个直接前趋ai-1和一个直接后继ai+1
顺序表
顺序表的定义
把逻辑上相邻的数据元素储存在物理上相邻的存储单元中的储存结构。
顺序存储结构占用一篇连续的存储空间
顺序表中元素储存位置计算
假设线性表的每个元素需要占l个存储单位,则第i+1个数据元素的储存位置和第i个数据元素的储存位置之间满足关系:
LOC(ai+1)=LOC(ai)+l
由此,所有数据元素的存储位置均可由第一个数据元素的储存位置得到:
LOC(ai) = LOC(a1)+(i-1)*l
线性表和数组的不同
相同点:
- 地址连续
- 依次存放
- 随机存取
- 类型相同
不同点:
- 线性表长可变(删除或添加)
- 数组长度不可动态定义
我们都知道,一维数组的定义方式,即
类型说明 数组[常量表达式],而常量表达式中可以包括常量和符号常量,不能包括变量。即C语言中不允许对数组大小作动态定义。
顺序表的实现
线性表定义
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include "time.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20 /* 存储空间初始分配量 */
typedef int ElemType; /* ElemType类型根据实际情况而定,这里假设为int */
typedef struct
{
ElemType *data; /* 数组,存储数据元素 */
int length; /* 线性表当前长度 */
}SqList;
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
初始化线性表
Status InitList(SqList *L)
{
L->data = (ElemType *)malloc(MAXSIZE*sizeof(ElemType));
if(!L->data){
printf("内存分配失败!");
exit(-1);
} // 内存分配失败
L->length=0;
return OK;
}
清空线性表
Status ClearList(SqList *L)
{
L->length=0;
return OK;
}
求线性表的长度
int ListLength(SqList L){
return (L.length);
}
判断线性表是否为空
Status ListEmpty(SqList L)
{
if(L.length==0) return TRUE;
else return FALSE;
}
顺序表的取值
Status GetElem(SqList L,int i,ElemType *e)
{
if(L.length==0 || i<1 || i>L.length) return ERROR;
*e=L.data[i-1];
return OK;
}
顺序表的查找
int LocateElem(SqList L,ElemType e)
{
int i;
if (L.length==0) return 0;
for(i=0;i<L.length;i++)
{
if (L.data[i]==e){
break;
}
}
if(i>=L.length) return 0;
return i+1;
}
顺序表的插入
Status ListInsert(SqList *L,int i,ElemType e)
{
int k;
if (L->length==MAXSIZE) /* 顺序线性表已经满 */
return ERROR;
if (i<1 || i>L->length+1)/* 当i比第一位置小或者比最后一位置后一位置还要大时 */
return ERROR;
if (i<L->length) /* 若插入数据位置不在表尾 */
{
for(k=L->length-1;k>=i-1;k--) /* 将要插入位置之后的数据元素向后移动一位 */
L->data[k+1]=L->data[k];
}
L->data[i-1]=e; /* 将新元素插入 */
L->length++;
return OK;
}
顺序表的删除
Status ListDelete(SqList *L,int i,ElemType *e)
{
int k;
if (L->length==0) /* 线性表为空 */
return ERROR;
if (i<1 || i>L->length) /* 删除位置不正确 */
return ERROR;
*e=L->data[i-1];
if (i<L->length) /* 如果删除不是最后位置 */
{
for(k=i;k<L->length;k++)/* 将删除位置后继元素前移 */
L->data[k-1]=L->data[k];
}
L->length--;
return OK;
}
输出顺序表中的所有数据
Status Output(SqList L)
{
int i;
for(i=0;i<L.length;i++)
visit(L.data[i]);
printf("\n");
return OK;
}
销毁线性表
void DestroyList(SqList *L){
if(L->data){
free(L->data);//释放空间
L->length=0;
}
}
测试
int main()
{
SqList L;
Status i;
ElemType e;
int j,k;
printf("-----初始化-----\n");
InitList(&L);
printf("初始化后,L的长度为 %d",L.length);
printf("\n-----向表中插入1-5的数据-----\n");
for(j=1;j<=5;j++)
i=ListInsert(&L,j,j);
printf("表中的数据为");
Output(L);
printf("表的长度为 %d\n",L.length);
printf("-----判断表是否为空-----\n");
i=ListEmpty(L);
printf("L是否空 %d(1:是 0:否)\n",i);
printf("-----清空表-----\n");
i=ClearList(&L);
printf("清空表后表的长度为 %d",L.length);
i=ListEmpty(L);
printf("此时表是否空:%d(1:是 0:否)\n",i);
printf("-----插入数据----- \n");
for(j=1;j<=10;j++){
ListInsert(&L,j,j);
}
printf("在L的表尾依次插入1~10后:表中的数据为");
Output(L);
printf("表长为 %d \n",L.length);
ListInsert(&L,1,0);
printf("在L的表头插入0后:表的数据为");
Output(L);
printf("表长为 %d \n",L.length);
printf("-----查找元素-----\n");
GetElem(L,5,&e);
printf("第5个元素的值为:%d\n",e);
printf("------判断是否有目标元素-----\n");
for(j=3;j<=4;j++)
{
k=LocateElem(L,j);
if(k)
printf("第%d个元素的值为%d\n",k,j);
else
printf("没有值为%d的元素\n",j);
}
printf("-----删除数据-----\n");
k=ListLength(L); /* k为表长 */
for(j=k+1;j>=k;j--)
{
i=ListDelete(&L,j,&e); /* 删除第j个数据 */
if(i==ERROR)
printf("删除第%d个数据失败\n",j);
else
printf("删除第%d个的元素值为:%d\n",j,e);
}
printf("依次输出L的元素:");
Output(L);
j=5;
ListDelete(&L,j,&e); /* 删除第5个数据 */
printf("删除第%d个的元素值为:%d\n",j,e);
printf("依次输出L的元素:");
Output(L);
printf("-----销毁表-----\n");
DestroyList(&L);
if(!L.data) printf("销毁成功!");
return 0;
}
思考
上文,一个基本的顺序表就实现了,但是,上面的顺序表是一个静态的顺序表,表的最大长度是固定的,使用每次变更长度,都需要我们手动的去修改,这样显然是不合理的,因此,我们也可以像下面这样去进行定义。
现在,顺序表结构的定义中,有了一个表的最大数量的属性,这样,当数据不够的时候,我们就可以重新去分配内存。
参考文献及视频
结语
好啦,本次分享就到这里。
文章如果有不正确的地方,欢迎指正,共同学习,共同进步。
若有侵权,请联系作者删除。