单链表
线性表的链式存储
线性表的链式表示又称为非顺序映像或链式映像
结点在存储器中位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻;链表中的逻辑次序和物理次序不一定相同;访问时只能通过头指针进入链表,并通过每个结点的指针域依次向后顺序扫描其他结点(顺序存取法),因此访问各个结点时间不同
用一组物理位置任意的存储单元来存放线性表的数据元素
链表:n个结点由指针链组成一个链表
结点:数据元素的存储映像
- 数据域:存储元素数值数据
- 指针域 :存储直接后继的存储位置
单链表/线性链表:结点只有一个指针域(后继结点地址)的链表;单链表由头指针唯一确定,因此单链表可以用头指针名字来命名
双链表:结点有两个指针域的链表(一个存放后继结点地址,一个存放直接前驱地址)
循环链表:首尾相连的链表

头指针:指向链表第一个结点的指针
首元结点:链表中存储第一个数据元素a1的结点
头结点:在链表的首元结点之前附设的一个结点;数据域内可以为空,也可以存放线性表长等附加信息,但此结点不能计入链表长度值
优:
-
便于首元结点的处理
-
便于空表和非空表的统一处理

空表:
-
带头结点
![image]()
-
不带头结点
![image]()
例:26个英文字母小写存储结构
逻辑结构(a,b,c,d,...,z)
链式存储结构

若使用链式存储结构:

单链表的定义
typedef struct Lnode{
int data;
struct Lnode *next;
}LNode,*LinkList;
单链表的初始化
int InitList_L(LinkList L){
L= (LinkList)malloc(sizeof(LNode));
L->next=NULL;
return 1;
}
单链表的是否为空
int ListEmpty(LinkList L){
if(L->next){
return 0;
} else return 1;
}
单链表的销毁(销毁头结点)
从头指针开始依次释放所有结点:P=L;L=L->next;free(P)
结束条件:L==NULL
int DestroyList_L(LinkList L){
LinkList p;
while (L){
p=L;
L=L->next;
free(p);
}
return 1;
}
清空单链表(保留头结点)
结束条件:p==NULL
int ClearList(LinkList L){
LinkList p,q;
p=L->next;
while(p){
q=p->next;
free(p);
p=q;
}
L->next=NULL;
return 1;
}
求单链表的表长
int ListLength_L(LinkList L){
LinkList p;
p=L->next;
int i=0;
while (p){
i++;
p=p->next;
}
return i;
}
单链表的取值(第i个元素的内容)
循环中的i++, ++i结果一样,但大量数据时++i性能比i++好,i++由于是在使用当前值之后再+1,需要一个临时的变量来转存;而++i则是在直接+1,省去了对内存的操作的环节
int GetElem(LinkList L, int i,int *e){
LinkList p;
p=L->next;
int j=1;
while(p&&j<i){
p=p->next;
j++;
}
if(!p||j>i){
return 0;
}
e=p->data;
return 1;
}
单链表的按值查找(返回地址)
LinkList LocateElem_L(LinkList L,int e){
LinkList p;
p=L->next;
while (p&&p->data!=e){
p=p->next;
}
return p;
}
单链表的按值查找(返回位置序号)
int LocatedElem_L(LinkList L,int e){
LinkList p;
p=L->next;
int j=1;
while (p&&p->data!=e){
p=p->next;
j++;
}
if(p){
return j;
}else return 0;
}
单链表的插入操作
在第i个结点前插入值为e的新结点:s->next=p->next;p->next=s;
int ListInsert_L(LinkList L,int i,int e){
LinkList p,s;
p=L;
int j=0;
while (p&&j<i-1){
p=p->next;
j++;
}
if(!p||j>i-1) return 0;
s->data=e;
s->next=p->next;
p->next=s;
return 1;
}
单链表删除第i个元素
p->next=p->next->next; 保存删除结点的数据域
int ListDelete(LinkList L,int i,int *e){
LinkList p,q;
p=L;
int j=0;
while (p&&j<i-1){
p=p->next;
j++;
}
if(!(p->next)||j>i-1) return 0;
q=p->next;
p->next=q->next;
*e=q->data;
free(q);
return 1;
}
单链表的插入和删除:不需要移动元素,只需要修改指针,时间复杂度为O(1),如果从头查找前驱结点时间复杂度为O(n)
单链表的查找:因只能顺序存取,在查找时要从头指针开始,查找时间复杂度为O(n)
头插法建立单链表
从一个空表开始将最后一个结点依次重复将各结点插入链表的头部(倒位序):p->next=L->next;L->next=p;
void CreateList_H(LinkList L,int n){
L=(LinkList)malloc(sizeof(LNode));
L=NULL;
for (int i = n; i >0 ; i--) {
LinkList p;
p=(LinkList)malloc(sizeof(LNode));
scanf((const char *) p->data);
p->next=L->next;
L->next=p;
}
}
算法时间复杂度是O(n)
尾插法建立单链表
从一个空表开始将最后一个结点依次重复将各结点插入链表的尾部,尾指针r指针链表的尾结点(正位序):p->data=ai;p->next=NULL;r->next=p;r=p;
void CreateList_R(LinkList L,int n){
LinkList r;
L=(LinkList)malloc(sizeof(LNode));
L->next=NULL;
r=L;
for (int i = 0; i <n ; i++) {
LinkList p;
p=(LinkList)malloc(sizeof(LNode));
scanf((const char *) p->data);
p->next=NULL;
r->next=p;
r=p;
}
}
算法时间复杂度是O(n)
线性表链式存储的合并
- 如用La的头结点作Lc的头结点:Lc=pc=La;
- 比较La与Lb表中值大小:pa->data<pb->data; pc->next=pa;pc=pa;pa=pa->next;
- 将剩余表中元素插入Lc:pc->next=pa?pa:pb;
typedef struct Lnode{
int data;
struct Lnode *next;
}LNode,*LinkList;
void MergeList_L(LinkList La,LinkList Lb,LinkList Lc){
LinkList pa,pb,pc;
pc=Lc=La;
pa=La->next;
pb=Lb->next;
while (pa&&pb){
if(pa->data<=pb->data){
pc->next=pa;
pc=pa;
pa=pa->next;
} else{
pc->next=pb;
pc=pb;
pb=pb->next;
}
}
pc->next=pa?pa:pb;
free(Lb);
}
时间复杂度是O(ListLength(La)+ListLength(Lb))


浙公网安备 33010602011771号