线性表,是零个或多个同类型数据元素的有限序列。
l首先,线性表是有限的序列,所以它是有开始和结束的,第一个元素无前驱,最后一个元素无后继。
l其次,现象表是有序的,所以元素之间是有顺序的,除了第一个和最后一个元素外,序列中的其他元素都有且只有一个前驱和后继。
线性表的存储结构存储结构,指的是数据在内存中的组织方式,线性表的存储结构有顺序存储和链式存储两种。
顺序存储线性表的顺序存储结构,指的是用一段连续的存储单元依次存放线性表中的数据元素。
线性表(a,a,......,an)的顺序存储示意图:
因为线性表中的数据元素是同类型的,C语言中,可以数组来实现线性表的顺序存储结构。
代码定义:
#ifndefHEADER_LIST_H_
#defineHEADER_LIST_H_
#defineMAXSIZE0//定义最多的数据元素个数
typedefintElemType;
typedefstruct
{
ElemTypedata[MAXSIZE];//数组存储空间
intlength;//表示线性表当前长度
}SqList;
#endif/*HEADER_LIST_H_*/
从上面的代码定义中,可以看出描述线性表顺序存储结构3个因素是:
)存储空间的起始位置:数组data的起始位置;
)线性表的最大存储容量:数组data的长度,在数组声明的时候被确定,是不可变;
3)线性表的当前长度:length,表示线性表中数据元素的个数,是可变;
顺序表读取操作/**
*从线性表L中用e返回第i个元素
*/
StatusGetElem(SqListL,inti,ElemType*e)
{
if(L.length==0
i
iL.length)
returnERROR;
*e=L.data[i-];
returnOK;
}
顺序表插入操作/**
*从线性表L的第i个位置后插入e
*/
StatusListInsert(SqList*L,inti,ElemTypee)
{
intk;
if(L-length==MAXSIZE
i
iL-length+)//检查插入位置合理性
returnERROR;
if(i=L-length)
{
for(k=L-length-;ki-;k--)//从表尾开始寻找到插入的位置
L-data[k+]=L-data[k];//从表尾开始每个元素都往后移动一位
}
L-data[i-]=e;//插入元素
L-length++;
returnOK;
}
顺序表删除操作/**
*从线性表L的第i个位置删除e
*/
StatusListDelete(SqList*L,inti,ElemType*e)
{
intk;
if(L-length==MAXSIZE
i
iL-length+)//检查删除位置合理性
returnERROR;
*e=L-data[i-];
if(iL-length)
{
for(k=i;kL-length;k++)//删除位置i后的每个元素都往前移一位
L-data[k-]=L-data[k];
}
L-length--;
returnOK;
}
顺序表的优缺点优点:
无须为表中的元素之间的逻辑关系增加额外存储空间,通过数组在内存中的连续分布的特点,在已经头元素位置的情况下可以快速读取表中任一位置的元素。
缺点:
线性表在声明的时候就要确定所需存储空间的大小,当线性表长度变化较大时,容易造成存储空间的碎片。另外,插入和删除操作需要移动大量的元素。
链式存储在顺序表中,由于相邻元素在内存中的位置也是相邻的,因此可以不必要存储元素之间位置的逻辑信息,但是在插入和删除的时候却需要移动大量的元素。
线性表的链式存储结构的特点是用一组任意的存储单元来保存数据元素,这组存储单元可以是连续的,也可以是不连续的,除了要存数据元素信息外,还要存储它的后继元素的地址。
存储数据元素信息的域称为数据域,存储直接后继位置的域称为指针域,这两部分加起来称为结点。
链表的存储示意图:
代码定义:
typedefstructNode
{
ElemTypedata;
Node*next;
}Node;
typedefstructNode*LinkList;
链表读取操作/**
*读取链表的第i个元素
*/
StatusGetElem(LinkListL,inti,ElemType*e)
{
intj=;
LinkListp;
p=L-next;
while(pji)
{
p=p-next;//P指针不断后移到读取的位置
++j;
}
if(!p
ji)
returnERROR;//第i个元素不存在
*e=p-data;
returnOK;
}
链表的插入操作/**
*在链表的第i个位置插入元素
*/
StatusListInsert(LinkList*L,inti,ElemTypee)
{
intj;
LinkListp,s;//p用来控制后移到第i个位置,s用来表示新生成的结点
p=*L;
j=;
while(pji)//循环后移到第i个位置
{
p=p-next;
++j;
}
if(!p
ji)
returnERROR;
s=(LinkList)malloc(sizeof(Node));//生成要插入的结点
s-data=e;
s-next=p-next;//插入链表中,这里的两句不可调换
p-next=s;
returnOK;
}
链表的删除操作/**
*在链表的第i个位置删除元素
*/
StatusListDelete(LinkList*L,inti,ElemTypee)
{
intj;
LinkListp,q;
p=*L;
j=;
while(p-nextji)//循环后移到第i个位置
{
p=p-next;
++j;
}
if(!(p-next)
ji)//第i个元素不存在
returnERROR;
q=p-next;
p-next=q-next;
*e=q-data;
free(q);
returnOK;
}
链表的生成操作(头插法)/**
*头插法生成链表
*/
voidCreateListHead(LinkList*L,intn)
{
LinkListp;
inti;
srand(time(0));
*L=(LinkList)malloc(sizeof(Node));//生成头结点
(*L)-next=NULL;//生成头结点
for(i=0;in;i++)
{
p=(LinkList)malloc(sizeof(Node));
p-data=rand()%00+;
p-next=(*L)-next;
(*L)-next=p;//头插法
}
}
链表的生成操作(尾插法)/**
*尾插法生成链表
*/
voidCreateListTail(LinkList*L,intn)
{
LinkListp,r;
inti;
srand(time(0));
*L=(LinkList)malloc(sizeof(Node));//生成头结点
r=*L;//r指向尾结点
for(i=0;in;i++)
{
p=(LinkList)malloc(sizeof(Node));
p-data=rand()%00+;
r-next=p;
r=p;
}
r-next=NULL;
}
链表整表删除操作/**
*链表的整表删除
*/
StatusClearList(LinkList*L)
{
LinkListp,q;
p=(*L)-next;
while(p)
{
q=p-next;//先要保存下一个结点的位置
free(p);
p=q;//循环后移
}
(*L)-next=NULL;
returnOK;
}
顺序表与链表的比较存储空间分布
时间性能
空间性能
查找
插入/删除
顺序表
连续
O()
O(N)
存储空间预分配
链表
离散
O(N)
O()
存储空间动态分配
所以,可以得出两个结论:
.如果线性表需要频繁查找,很少进行插入和删除操作时,适合采用顺序存储;如果需要频繁插入和删除时,则适合采用链式存储。
.如果事先知道线性表的长度,且变化不大时,适合采用顺序存储;如果事先并不知道线性表的长度时,且线性表的元素个数变化较大时,则适合采用链式存储。
治疗白癜风最好的中药治疗白癜风最好的中药