顺便提一下,之前说的顺序存储结构由于其两个元素在物理位置上相邻,故它的优点是很方便存取,但与此带来的缺点是如果进行插入或者删除操作的话,需要移动大量的元素,在工程量大的时候,很耗时间。链式存储结构不要求元素的物理位置相邻,不存在顺序存储的弱点,但也同时失去顺序表可随机存取的优点。
目录
对于链表的数据元素,它除了要存储本身的信息外,还要存储一个能够指示其直接后继的信息(即指向后继的存储位置),逻辑上称这部分数据元素为结点,结点包括两个域:数据域,主要存储本身的数据元素信息。指针域,是用来存储其直接后继的存储位置,这部分信息称作指针。链表一般分为:单链表(结点只有一个指针域的链表)、双链表(有两个指针域的链表)、多链表(有多个指针域的链表)、循环链表(首尾相接的链表),一般都是说的单链表。
单链表的特点:
- 数据元素的逻辑顺序和物理顺序不一定相同。
- 在查找数据元素时,必须从头指针开始依次查找,表尾的指针域指向NULL。
- 在特定的数据元素之后插入或删除元素,不需要移动数据元素,因此时间复杂度为 O(1)。
- 存储空间不连续,数据元素之间使用指针相连,每个数据元素只能访问周围的一个元素。
- 长度不固定,可以任意增删。
单链表的存取必须从头指针开始进行,头指针表示指向链表中第一个结点(头结点或者首元结点)的指针,是一个具体的地址,同时,由于最后一个数据元素没有直接后继,链表最后一个结点的指针应设为“空”(NULL),其长度变化较大,主要用于插入和删除操作。
为了增加可读性,一般我们在单链表的第一个结点前附设一个结点,称作头结点,头结点的数据域可以不存任何信息,也可以存储如线性表的长度等类的附加信息,头结点的指针域存储指向第一个结点的指针(即第一个数据元素的存储位置)。首元结点是指链表中存储单链表第一个数据元素的结点。
其链表的存储结构为:
typedef int Status;
typedef int ElemType;
typedef struct LNode{
ElemType data ;
struct LNode *next ;
}LNode,*LinkList;
初始化
为头结点构造一个空间
Status InitList(LinkList &L){
L = (LinkList)malloc(sizeof(LNode));
if(!L)
exit(OVERFLOW) ;
L->next=NULL ;
}
清空链表
前提是链表已存在(后续操作均在此基础上进行)
Status ClearList(LinkList &L){
if(L==NULL){
cout<<"该链表不存在。"<<endl;
return TRUE;
}
LinkList p ,q ;
p=L->next;
while(p!=NULL){
q=p ;
p=p->next;
free(q);
}
L->next=NULL;
//cout<<"链表已被清空。"<<endl;
return TRUE ;
}
销毁链表
和清空不同的是注意要把头结点一块销毁。
Status DestroyList(LinkList &L){
LinkList p,q ;
p=L;
while(p){
q=p->next;
free(p);
p=q;
}
L=NULL ;
return TRUE ;
}
判断链表是否为空
Status ListEmpty(LinkList L){
if(L->next==NULL){
cout<<"链表是空的。"<<endl;
return TRUE ;
}
else{
cout<<"链表中尚有数据元素,不是空表。"<<endl;
return FALSE ;
}
}