定义:具有相同特性的数据元素的一个有限序列
特点:有穷性,一致性,序列性
在非空的线性表中,有且仅有一个开始结点a1,没有直接前趋,仅有一个直接后继a2
有且只有一个终端结点an,它没有直接后继,只有一个直接前趋an-1
其余的内部结点ai都有且仅有一个直接前趋ai-1和一个直接后继ai+1
抽象数据类型线性表的定义
ADT List{
数据对象:D={ai | ai属于Elemset,(i=1,2,···n,n>=0)}
数据关系:R={<ai-1,ai> | ai-1,ai属于D,(i=2,3,···,n)}
基本操作:
InitList(&L):构造一个空的线性表L,可以往线性表中存储数据
DestroyList(&L)
初始条件:线性表L已经存在
操作结果:销毁线性表,内存之中删除
ClearList(&L)
初始条件:线性表L已经存在
操作结果:将线性表L重置为空表
ListEmpty(L)
初始条件:线性表L已经存在
操作结果:若线性表为空返回TRUE,否则返回FALSE
ListLength(L)
初始条件:线性表L已经存在
操作结果:返回线性表L元素的个数
GetElem(L,i,&e)
初始条件:线性表L已经存在
操作结果:用e返回线性表L中第i个数据元素的值
LocateElem(L,e,compare())
初始条件:线性表已经存在,compare()是数据元素判定函数
操作结果:返回L中第一个与e满足compare()的数据元素的位序,不存在则返回值为0
ListInsert(&L,i,e)
初始条件:线性表已经存在
操作结果:在L的第i个位置之前插入新的数据元素e,L的长度加1
ListDelete(&L,i,&e)
初始条件:线性表已经存在
操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1
PriorElem(L,cur_e,&pre_e)
初始条件:线性表已经存在
操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回他的前驱,否则操作失败,pre_e无意义
NextElem(L,cur_e,&next_e)
初始条件:线性表已经存在
操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回他的后继,否则操作失败,next_e无意义
ListTraverse(&L,vistted())
初始条件:线性表已经存在
操作结果:依次对线性表中每个元素调用visited(),线性表的遍历
} ADT List
线性表有两种基本的存储结构单位
顺序存储结构和链式存储结构
顺序表
线性表的顺序存储结构
顺序存储定义:把逻辑上相邻的数据元素存储在物理上相邻的存储单元中的存储结构。
线性表顺序存储结构占用一片连续的存储空间,知道某一个元素的存储位置就可以计算出其他元素的存储位置。
-
假设顺序表中所有结点的类型相同,则每个结点所占用存储空间的大小亦相同,每个结点占用c个存储单元。其中第1个单元的存储地址则是该结点的存储地址,并设顺序表中开始结点a1的存储地址(简称为基地址)是LOC(a1),那么结点ai的存储地址LOC(ai)可通过下式计算得到:
LOC(ai)=LOC(a1)+(i−1)∗c (1≤i≤n)
线性表定义模板
线性表的五个基本运算有插入、删除、修改 、查找以及线性表合并 。
插入:在顺序表L上的第i个元素位置上插入新元素e,如果i值正确,则返回FALSE,否则将原来的第i个元素以及后面的元素都向后移动一位,并从最后一个元素an开始移动。
元素移动的次数与表长和插入位置有关,共有n+1个可以插入的元素。当i=n+1时,即插入在末尾,移动次数为0;当i=1时,插入在开头,移动次数达到最大值n。一般需要将ai-an的元素均后移到一个位置时,移动次数为n-i+1。平均次数为2/n。 插入1个新结点之后,线性表L的长度将变为n+1。
删除:删除顺序表L的第i个元素,如果i值不正确,返回FALSE,否则将线性表的第i个元素以后的元素均向前移动一个位置,并从ai+1开始移动起来。
当i=n时,删除末尾元素,移动次数为0,当i=1时,删除开头,移动次数为0,一般需要将ai-an的元素均前移到一个位置时,移动次数为n-i。平均次数为2/n-1。 删除1个结点之后,线性表L的长度将变为n-1。
链表
线性表的链式存储结构称为链表。
定义:链表结构是一种动态存储分配的结构形式,可以根据需要动态申请所需要的内存单元
最常用的链表结构:单链表,双链表,循环链表
链表中每个结点都包括如下两个部分:
- 数据部分:保存该结点的实际数据
- 地址部分:保存的是下一个结点的地址
单链表
定义:每个结点中只包含一个指针。
typedef struct LNode
{ ElemType data; //存放元素值
struct LNode * next; //指向后继结点
}LinkNode //单链表结点类型
插入结点
单链表的两个数据域分别为a和b的结点,(已知a结点的指针为p)在a和b之间插入一个数据域为x的结点(s指向他)
插入前
首先是让x结点的指针域(s->next)指向b结点(p->next),然后让a结点的指针域(p->next)指向x结点(s),从而实现3个结点之间逻辑关系的变化
插入后
s->next = p->next;
p->next = s;
注意:这两个语序不能颠倒
删除结点
有a,b,c三个结点。在单链表中删除结点p的后继结点,即删除b结点。
首先让a结点的指针域(p->next)指向c结点(p->next->next)
q=p->next; //q临时保存被删结点
p->next=q; //从链表中删除结点q
free(q); //释放结点q的空间
建立单链表
1.头插法
从一个空表开始依次读取数组a中的元素,生成一个新的结点s,将读取的数组元素存放到该结点的数据域中,然后将其插入到当前链表的表头上。
2.尾插法
该方法从一个空表开始依次读取数组a中的元素,生成一个新的结点s,将读取的数组元素存放到该结点的数据域中,然后将其插入到当前链表的表尾上。
双链表
定义:每个结点中包含两个指针,一个指向上一个结点,一个指向下一个结点。
建立双链表采用头插法和尾插法整体和单链表相似。
双链表的插入操作
s->next = p->next;
p->next->prior=s;
s->prior = p;
s->next = p;
双链表的删除操作
p->next=q->next;
q->next->prior=p;
循环链表
循环链表是另外一种链式存储结构。
循环链表有循环单链表和循环双链表。
循环单链表是将单链表的尾结点next指针域由原来空改为指向头结点,整个单链表形成一个圆。
循环双链表是将双链表的尾结点next指针域由原来空改为指向头结点,将头结点prior指针域改为指向尾结点,整个双链表形成两个圆。
头指针:每个链表带有一个头结点,并通过头结点的指针指向唯一标识该链表。
首指针:指向首结点或开始结点的指针
尾指针:指向尾结点的指针
指针域:链表中的每个存储结点不仅包括元素本身的信息,而且包括表元素之间的逻辑关系的信息,在c中用指针来实现,称为指针域。
链表和顺序表的比较
顺序表插入与删除操作费时,具有随机存储特性。
链表修改相关结点的指针域即可,方便又省时,不具有随机存储信息。
顺序表的存储密度较高,存储密度=结点中数据元素所占的存储量和整个结点占用的存储量之比。一般情况下,存储密度越大,存储空间的利用率越高。
有序表
有序表的所有元素以递增或者递减的方式有序排列,有序表为线性表。