目录
一、链表概述
1.相关定义
- 链表:线性表的链式存储结构称为链表
- 单链表:在每个结点中除包含有数据域以外只设置一个指针域,用于指向其后继结点,这样构成的链表称为线性单向链接表,简称为单链表。
- 双链表:在每个节点中除包含有数值域以外设置两个指针域,分别用于指向其前驱结点和后继结点,这样构成的链表称为线性双向链表,简称为双链表。
- 头指针:在线性表的链式存储中,通常每个链表带有一个头结点,并通过头结点的指针唯一标识该链表,称之为头指针。
- 首指针:指向首结点或者开始结点的指针称为首指针。
- 尾指针:指向尾结点的指针称为尾指针
二、单链表
在单链表中,假设每个结点的类型用LinkNode表示,它应包括存储元素的数据域,这里用data表示,其类型用通用类型标识符ElemType表示,还包括存储后继结点位置的指针域,这里用next表示。LinkNode类型的声明如下:
typedef struct LNode
{
ElemType data; //存放元素值
struct LNode *next; //指向后继结点
}LinkNode; //单链表结点类型
为了简单,假设ElemType为int类型,使用以下自定义类型语句:
typedef int ElemType;
1.插入和删除节点的操作
(1)插入结点
s->next=p->next;
p->next=s;
(2)删除结点
p->next=p->next->next;
一般情况下,在删除一个结点后还需要释放其存储空间,实现删除上述b结点并释放其存储空间的语句描述如下 :
q=p->next; //q临时保存被删结点
p->next=q->next; //从链表中删除结点q
free(q); //释放结点q的空间
2.建立单链表
(1)头插法
void CreatList(LinkNode *&L,ElemType a[],int n)
{
LinkNode *s;
L=(LinkNode *)malloc(sizeof(LinkNode));
L->next=NULL; //创建头结点,其next域置为NULL
for(int i=0;i<n;i++) //循环建立数据结点s
{
s=(LinkNode *)malloc(sizeof(LinkNode)):
s->data=a[i]; //创建数据结点s
s->next=L->next; //将结点s插入到原首结点之前,头结点之后
L->next=s;
}
}
(2)尾插法
void CreatListR(LinkNode *&L,ElemType a[],int n)
{
LinkNode *s,*r;
L=(LinNode *)malloc(sizeof(LinkNode)); //创建头结点
r=L; //r始终指向尾结点,初始时指向头结点
for(int i=0;i<n;i++) //循环建立数据结点
{
s=(LinkNode *)malloc(sizeof(LinkNode));
s->data=a[i]; //创建数据结点s
r->next=s; //将结点s插入到结点r之后
r=s;
}
r->next=NULL; //尾结点的next域置为NULL
}
3.线性表基本运算在单链表中的实现
(1)初始化线性表InitList(&L)
void InitList(LinkNode *&L)
{
L=(l=LinkNode *)malloc(sizeof(LinkNode));
L->next=NULL; //创建头结点,其next域置为NULL
}
(2)销毁线性表DestroyList(&L)
该运算释放单链表L占用的内存空间,即逐一释放全部结点空间。
void DestroyList(LinkNode *&L)
{
LinkNode *pre=L,*p=L->next; //pre指向结点p的前驱节点
while(p!=NULL) //扫描单链表L
{
free(pre); //释放pre结点
pre=p; //pre、p同步后移一个结点
p=pre->next;
}
free(pre); //循环结束时p为NULL,pre指向尾结点,释放它
}