线性表:是由n(n>=0)个类型相同的数据元素组成的有限序列。
-数据元素可以是一个数、一个符号、一本书等。
-通常表示成 L=(a1,a2,……,an-1,an)
-其中:L为线性表名称,一般用大写;an为组成该线性表的数据元素,小写。
-线性表的长度:线性表中数据元素的个数,当n=0时,线性表为空,又称为空线性表。
-相邻数据存在序偶关系。
非空线性表:
-存在唯一的称为“第一个”的元素;
-存在唯一的称为“最后一个”的元素;
-除第一元素外,每个元素都有且只有一个前驱元素;
-除最后一个元素外,每个元素都有有一个后续元素。
ADT List{
数据对象:D={ai|ai∈ElemSet,i=1,2,...,n>=0}
数据关系:R={<ai-1,ai>|ai-1,ai∈D,i=2,3,…,n}
基本操作:线性表的初始化操作、插入操作、删除操作、检索操作、取元素操作…
}
InitList(&L) 初始化线性表
ListEmpty(L) 判断线性表L是否为空
ListLength(L) 求线性表的长度
GetElem(L,i,&e) 获取线性表L中的某个数据元素的内容
LocateElem(L,e) 检索值为e的数据元素
PriorElem(L,e) 返回线性表L中e的直接前驱元素
NextElem(L,e) 返回线性表L中e的直接后继元素
ListInsert(&L,i,e) 在线性表L中插入一个数据元素
ListDelete(&L,i,e) 删除线性表L中第i个数据元素
线性表的表示和实现
顺序表示(顺序表):是指用一组地址连续的存储单元依次存储线性表中的每个数据元素。
>以数组作为基础实现。
>数组的元素个数固定,线性表的元素个数可随操作改变。
>数组大小确定了顺序表的最大容量。
>利用数据元素的存储位置表示线性表中相邻数据元素之间的前后关系,即线性表的逻辑结构与存储结构一致。
-优点
>在访问线性表时,可以利用上述给出的数学公式,快速计算任何一个数据元素的存储地址。即访问每个数据元素所花费的时间相等。
>无须为表示表中元素之间的逻辑关系而增加额外的存储空间。
>这种存取元素的方法被称为随机存取法。使用这种存取方法的存储结构被称为随机存储结构。
-缺点
>插入和删除操作需要移动大量元素。
>当线性表长度变化较大时,难以确定存储空间的容量。
-实现
const LIST_INIT_SIZE=100; //表示初始分配空间
const LISTINCREMENT=10; //空间分配增量
typedef struct{
ElemType *elem;//存储空间
int length; //当前长度
int listsize; //当前存储容量
}SqList
注:数组指针elem指示线性表的基地址,length:线性表的当前长度。
C语言数组的下标从0开始,即数组中的第i个元素为L.elem[i-1]
-初始化线性表L
>为顺序表分配一个预定义大小的数组空间,并将线性表的初始长度设为0
Status InitList_Sq(SqList &L){ //构造空表L
L.elem=(ElemType *)malloc(LIST_INIT_SIZE*sizeof(ElemType));
if(!L.elem) exit(OVERFLOW);
L.length=0;
L.listsize=LIST_INIT_SIZE;
return OK;
} //InitList_Sq
-求线性表L的长度
>线性表中数据元素的个数
int ListLength_Sq(SqList L){
return L.length;
}//ListLength_Sq
-判断线性表L是否为空
int ListEmpty_Sq(SqList L){
if(L.length==0) return TURE;
else return FALSE;
}//ListEmpty_Sq
-获取线性表L中的第i个数据元素的内容
>当第i个元素存在时,将值返回给e,否则返回ERROR
Status GetElem_Sq(SqList L,int i,ElemType *e){
if((i<1)||(i>L.length)) return ERROR;
*e=L.elem[i-1]; return OK;
}//GetElem_Sq
-在线性表的第i个位置上插入一个元素 时间复杂度O(n) 移动大约一半元素
>即在第i-1个元素和第i个元素之间插入
>将第i个元素之后的所有元素后移,将新元素放置在第i个位置。
Status ListInsert_Sq(SqList &L,int i,ElemType e){
if((i<1)||(i>L.length+1)) return ERROR; //非法的位置
if(L.length>=L.listsize) error(‘OVERFLOW’;) //溢出
for(j=L.length-1;j>=i-1;--j)
L.elem[j+1]=L.elem[j]; //第i个元素之后的所有元素后移
L.elem[i-1]=e;
L.length++;
return OK;
}//ListInsert_Sq
-将线性表L中的第i个数据元素删除 时间复杂度O(n) 移动大约一半元素
>元素逐个前移,覆盖掉被删除的元素,需要维护元素个数记录
Status ListDelete_Sq(SqList &L,int i,ElemType &e){
if((i<1)||(i>L.length)) return ERROR;
ElemType *p=&L.elem[i-1]; e=*p;
q=L.elem+L.length-1;
for(++p;p<=q;++p) *(p-1)=*p;
--L.length; return OK;
}//ListDelete_Sq
链式表示(链表):用一组任意的存储单元存储线性表
>存储单元不要求连续:物理结构不反应逻辑结构。
>不可以随机存取,但插入和删除方便
>需要两个域(表示一个结点):一个指数据本身;一个指数据元素间的先后关联。
>结点中表示关联的部分为指针域,内部存放指针或链。n个结点链接成一个链表。
-单链表:为每个元素关联一个链接,表示后继关系。与表中n个元素对应的n个结点通过指针链接成一个链表。链表中每个结点只有一个指针域的存储映像。
>结点:元素的存储映像,包括数据域和指针域。
>头指针:指向单链表第一个结点的存储位置的指针,整个链表的存取必须从头指针开始。无论链表是否为空,头指针均不为空。是链表的必要元素。
>头结点:包含数据域的头指针。其数据域一般无意义,是非必要元素。
>头结点的优点:
-无头结点,在表头插入删除时需要修改表头指针,在其他位置插入删除时修改其前驱结点的next。
-有头结点,操作的实现可以统一处理,因为表中“有效”结点都链接在另一结点的next域。
-设L是LinkList类型的变量,L指向单链表的首结点,当L=null时,表为空。
-当L为头结点时,L->next=null时表为空。
-实现(C语言)
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode,*LinkList;
-初始化线性链表 // L为带头结点的单链表的头指针
Status InitList_L(LinkList &L){
L=(LinkList)malloc(sizeof(LNode));
L->next=null;
return OK;
}//InitList_L
-获取单链表中第i个元素 //L为带头结点的单链表的头指针
Status GetElem_L(LinkList L,int i,ElemType &e){
p=L->next; j=1;
while(p&&j<i){
p=p->next; ++j;
}
if(!p||j>i) return ERROR;
e=p->data; return OK;
}//GetElem_L
-在链表L中第i个数据元素之前插入数据元素e //指针更新次序不能错
Status InsertElem_L(LinkList &L,int i,ElemType e){
p=(LinkList)malloc(sizeof(LNode));
if(!p) exit(‘OVERFLOW’);
p->data=e;
q=L;j=0;
while(q&&j<i-1){q=q->next;++j;}
if(!q||j>i-1) return ERROR;
p->next=q->next;q->next=p; return OK;
}//InsertElem_L
-在链表L中删除第i个数据元素
Status DeleteElem_L(LinkList L,int i,ElemType &e){
LinkList p=L,q; int j;
if((i<1)||(i>ListLength(L))) return ERROR;
for(j=0;j<i;j++) p=p->next;
q=p->next;
e=q->data;
p->next=q->next;
free(q);
return OK;
}//DeleteElem_L
-定位操作(在L中找到第一个值和e相同的结点,返回其地址,不存在返回空值)
Status LocateElem_L(LinkList L,ElemType e){
if(!L) return NULL;
p=L;
while(p&&p->date!=e) p=p->next;
return p;
}//LocateElem_L
-循环链表:线性表的另一种链式存储结构。
>从表的任何位置除法,都可以找到其他结点
>判空:head=head->next;
-双向链表:每个结点有两个指针域:一个指向直接后继,另一个指向直接前驱。
-存储结构
typedef struct DoubleLNode{
ElemType data;
struct DoubleLNode *prior;
struct DoubleLNode *next;
}DoubleLNode,*DoubleLinkList;
-双向循环链表:循环链表+双向链表,详略。
-链表表示的一元多项式定义 //可根据需求自由扩展数据域或指针域
typedef struct PNode{
float coef; //浮点数-系数
int expn; //整数-指数
struct PNode *next;
}ElemType,term;
typedef LinkList polynomial;
-多项式的链表的建立
Void CreatePolyn(polynomail &p,int m){
InitList(p); h=GetHead(p);
e.coaf=0.0; e.expn=-1;
SetCurltem(h,e);
for(i=1;i<=m;i++){
scanf(e.coef,e.expn);
if(!LocateElem(p,e,q,(*cmp)())){
if(MakeNode(s,e)) InsFirst(q,s);
}
}//for
}//CreatePolyn