基本定义
单链表中每个结点存放一个数据元素,并用一个指针表示结点间的逻辑关系,即每个结点的指针域存放后继结点的地址(线性表中每个元素有唯一的后继元素)。因此单链表的一个存储结点包含两个部分,结点的基本形式如下:
其中,data部分称为数据域,用于存储线性表的一个数据元素,也就是说在单链表中一个结点存放一个数据元素;next部分称为指针域或链域,用于指向后继元素对应的结点。
单链表中尾结点之后不再有任何结点,那么它的 next 域设置为什么值呢?有以下两种方式:
- 将尾结点的next域用一个特殊值 NULL ,这样的单链表为非循环单链表。通常所说的单链表都是指这种类型的单链表
- 将尾结点的 next 域指向头结点,这样可以通过尾结点移动到头结点,从而构成一个查找环,将这样的单链表称为循环单链表
假设数据元素的类型为 ElementType ,单链表的结点类型声明如下:
typedef struct node{
ElementType data; //数据域
struct node *next; //指针域
}SLinkNode; //单链表结点类型的申明
基本运算
先讨论线性表基本运算在单链表上的实现。
在带头结点的单链表中,头结点的data域通常不存储任何信息,头结点的 next 域指向第一个数据结点,即存放第一个数据结点的地址。通过头结点的指针L(称为头指针)来标识整个单链表。头结点指针称为头指针 ,第一个数据结点称为首结点,首结点指针称为首指针(对于不带头结点的单链表,一般是通过首指针标识单链表)。最后一个结点称为尾结点,尾结点指针称为尾指针。
1,初始化线性表运算算法
创建一个空的单链表,它只有一个头结点,由L指向它。该结点的next域为空,data 域未设定任何值。算法如下:
void InitList(SLinkNode *&L){ //L 为引用型参数
L = (SLinkNode *) malloc(sizeof(SLinkNode)); //创建头结点L
L->next=NULL; //头结点next 置为空表示空单链表
}
2,销毁线性表运算算法
一个单链表 L 中的所有结点空间都是通过 malloc 函数分配的(即程序员自己手工分配的),在不再需要 L 时系统不会自动释放这些结点空间,程序员必须通过调用 free函数 释放所有结点的空间(即程序员自己手工分配的空间需要程序员自己手工释放)。算法如下:
void DestroyList(SLinkNode *&L){
SLinkNode *pre=L,*p=pre->next;
while(p!=NULL){
free(pre); //释放pre结点空间
pre=p;
p=p->next; //pre、p同步后移
}
free(pre); //释放pre 指向的尾结点空间
}
移动次数与单链表L 中数据结点的个数有关,故其时间复杂度为 O(n)。
3,求线性表长度运算
不同于顺序表,在单链表中没有直接保存长度信息,需要通过扫描方式求长度。对应算法如下:
int GetLength(SLinkNode *L){
int i=0;
SLinkNode *p=L->next; //p指向头结点,i置为0
while(p!=NULL){
i++;
p=p->next; //p移到下一个结点,i++
}
return i; //p 为空是,i即是数据结点的个数
}
移动次数与单链表L 中数据结点的个数有关,故其时间复杂度为 O(n)。
4,求线性表中第i个元素运算
这个直接从头开始扫描遍历直到第i个就行了,下面直接看代码:
int GetElem(SLinkNode *L,int i,ElemType &e){
int j=0;
SLinkNode *p=L; //p指向头结点,计数器j置为0
if(i<=0)
return 0;
while(p!=NULL&&j<i){
j++;
p=p->next;
}
if(p==NULL)
return 0;
else{
e=p->data;
return 1; //找到后返回1
}
}
在这里我们是否想到了在顺序表中进行该算法时,直接按序索取就行了,表明顺序表具有随机存取特性。由此可见,单链表不具有随机存取的特性,其时间复杂度为 O(n)。
5,按值查找运算算法
顾名思义,就是在单链表L中从首结点开始查找第一个值域与e相等的结点,若存在这样的结点,则返回其逻辑序号,否则返回0。对应算法如下:
int Locate(SLinkNode *L,ElemType e){
SLinkNode *p=L->next;
int j=1; //p指向首结点,j置为其序号1
while(p!=NULL&&p->data!=e){
p=p->next;
j++;
}
if(p==NULL)
return 0;
else
return j; //找到后返回其序号
}