目录
一.前言
线性表的链式表示又称为非顺序映像或链式映像。简而言之,链表可以理解为由指针链连接的n个结点组成的。其中每一个结点包括数据域和指针域。值得注意的是,与顺序表不同,链表中的逻辑次序与物理次序不一定相同。
二.单链表的定义和结构
我们称结点只有一个指针域的链表称为单链表或线性链表。注意区分我们后面要学习的双向链表和循环链表。下面我们来看下单链表的组成,单链表可以是这样的结构:
它包含一个头指针,头结点以及所有的结点,其中存储的第一个数据元素的结点称为首元结点。
其中,头指针是必要的,头结点可有可没有。当我们的单链表没有头结点的时候,头指针为空时表示这个单链表是空表。有头结点的时候,头结点的指针域为空时表示空表。
由于单链表是由表头唯一确定的,所以单链表可以用头指针的名字来命名,若头指针是L,则把链表称为表L。下面我们来定义一个单链表类型:
typedef struct Lnode{
ElemType data; //结点的数据域
struct Lnode* next //结点的指针域,指向这个struct Lnode的数据类型
}Lnode,*LinkList; //LinkList为指向结构体Lnode的指针类型
在完成了这一步之后,我们通常使用LinkList L 来定义一个链表L。使用Lnode* p来定义一个结点指针p。接着我们对定义好了的单链表来进行初始化:
Status InitList_L(LinkList &L){
L=new Lnode; //给链表L分配内存空间
L->next=NULL; //初始化为一个空表
return OK;
}
其中Status就表示一个数据类型,在我之前的博客里面关于顺序表的实现与操作中就有提到。Status后面的InitList_L就表示这个初始化函数的函数名,可以由我们自行定义,但最好是能够见名思意的。
三. 单链表的操作
1.判断链表是否为空
所谓空表就是链表中没有元素,但是头指针和头结点仍然存在。这个算法的核心思路就是判断头结点指针域是否为空,如下所示:
Status ListEmpty(LinkList L){
if(L->next) //表示不是空表,返回0
return 0;
else //若是空表则返回1
return 1;
}
其中,L->next就表示头结点的指针域,而我们的头指针则就是用L表示的。
2. 单链表的销毁
就是从头指针开始,依次释放所有结点。所以可以利用一个循环来实现,循环结束的条件也就是指针指向空。表示没有结点可以再释放了。如下所示:
Status DestroyList_L(LinkList &L){
Lnode* p; //定义一个Lnode类型的指针p
while(L){ //从头结点开始
p=L;
L=L->next; //指向下一个结点
delete p; //释放结点
}
return OK;
}
值得注意的是,我们类C语言中要想使用delete来释放一个空间,就得使用new来分配空间。
3. 单链表的清空
就是指链表仍然存在,但是链表中已经没有元素,成为了一个空链表(头指针和头结点仍然存在)
如下所示:
Status ClearList(LinkList &L){
Lnode*p,*q; //定义两个Lnode类型的指针
p=L->next;
while(p){ //循环结束条件就是p指向空的时候
q=p->next;
delete p;
p=q;
}
L->next=NULL; //头结点指针域为空
return OK;
}
4. 求单链表的表长
如下所示:
Status ListLength(LinkList L){
LinkList p;
p=L->next; //p指向第一个结点
i=0;
while(p){ //遍历单链表,统计结点数
i++;
p=p->next;
}
return i;
}
5. 单链表的取值
就是取单链表中第i个元素的内容。可以从链表的头指针出发,顺着链域next逐个结点往下搜索,直到搜索到第i个结点为止。因此,链表不是随机存取结构。
Status GetElem(LinkList L,int i,ElemType &e){
Lnode* p;
int j=1;
p=L->next;
while(p&&j<i){ //向后扫描,直到p为空或者指向到第i个元素为止
p=p->next;
++j;
}
if(!p||j>i) return ERROR; //如果p指向空了或者j>i,表示链表中并没有这个元素
e=p->date; //取第i个元素的数据,并通过e返回
return OK;
}
6. 单链表的查找
单链表的查找可分为两种,第一种就是根据指定数据获取该数据所在的位置(地址)。第二种则是根据指定数据获取该数据的位置序号。
如下所示,首先介绍第一种:
Lnode* LocateElem(LinkList L,ElemType e){
p=L->next;
while(p&&p->data!=e)
p=p->next;
return p; //如果找到了就返回对应的地址,如果没找到,也就是p指向空的时候,返回NULL
}
第二种如下:
Status LocateElem(LinkList L,ElemType e){
Lnode* p;
p=L->next;
int j=1;
while(p&&p->data!=e){
p=p->next;
j++
}
if(p) return j; //如果找到,返回L中值为e的数据元素的位置序号。
else return 0; //否则返回0,表示没有找到
}
7. 单链表的插入
在第i个结点前插入值为e的新结点。核心思路就是使新结点的指针域指向结点a(i),使结点a(i-1)的指针域指向新结点。如下所示:
Status ListInsert(LinkList &L,int i,ElemType e){
Lnode* p;
p=L;
int j=0;
while(p&&j<i-1){ //寻找第i个结点,p指向i-1结点
p=p->next;
++j
}
if(!p||j>i-1) return ERROR; //i大于表长+1或者小于1,插入位置非法
s=new Lnode; //生成新结点s
s->data=e; //新结点s的数据域为e
s->next=p->next; //将结点s插入到L中去
p-next=s;
return OK;
}
8. 单链表的删除
就是删除第i个结点,如下所示:
Status ListDelete(LinkList& L,int i,ElemType &e){
Lnode* p;
p=L;
int j=0;
while(p->next&&j<i-1){ //寻找第i-1个结点,并让p指向它。
p=p->next;
++j;
}
if(!(p->next)||j>i-1) return ERROR; //删除位置不合理
q=p->next; //临时保存被删除结点的地址以备释放
p->next=q->next; //改变要被删除结点前驱结点的指针域
e=q->data; //保存删除结点的数据域
delete q; //释放删除结点的空间
return OK;
}