单链表的介绍
背景及其概念
由于顺序表的插入、删除操作需要移动大量的元素,影响了运行效率,由此引入了线性表的链式存储。链式存储线性表时,不需要使用地址连续的存储单元,即它不要求逻辑上相邻的两个元素在物理地址上也相邻,它通过“链”建立起数据元素之间的逻辑关系,因此对线性表的插入、删除不需要移动元素,而只是需要修改指针。
线性表的链式存储又称单链表,它是指通过依据任意的存储单元来存储线性表中的数据元素。
为了建立数据元素之间的线性关系,对每个链表结点,除存放元素自身的信息外,还需要存放一个指向后继的指针。
单链表不同于顺序表,没有了随机存取的特点,当查找某个特定的结点时,需要从表头开始遍历,依次查找。
单链表的结点结构
相比于顺序表而言,单链表的结点==数据域+指针域。
数据域用来存放元素信息;
指针域用来存放其后继结点的地址;
typedf struct LNode{ //定义单链表结构类型
ElemType data; //每个节点存放一个数据元素
struct LNode *next; //指针指向下一个节点
}LNode,*LinkList;
//此处不了解的可以前往我的专栏数据结构的上一篇,来学习typedf——结构体——以及相关定义使用方法。
单链表定义的代码实现
不带头节点的单链表
typedf struct LNode{ //定义单链表结构类型
ElemType data; //每个节点存放一个数据元素
struct LNode *next; //指针指向下一个节点
}LNode,*LinkList;
//初始化一个空的单链表
bool Initlist(LinkList &L){
L=NULL;//空表,暂时还没有任何节点,可以防止脏数据
return true;
}
void test (){
LinkList L; //声明一个指向单链表的指针
//注意到此处并没有创建一个节点
//初始化一个空表
InitList(L);
//....后续代码。。。。。
}
```c
//判断单链表是否为空
bool Empty(LinkLIst L){
if (L==NULL)
return true;
else
return falsr;
}
//or
bool Empty(LinkList L){
return (L==NULL);
}
带头结点的单链表
typedf struct LNode{ //定义单链表结构类型
ElemType data; //每个节点存放一个数据元素
struct LNode *next; //指针指向下一个节点
}LNode,*LinkList;
//初始化一个空的单链表(带头节点 )
bool Initlist(LinkList &L){
L = (LNode *)malloc(sizeof(LNode));//分配一个头节点,此头结点不存储数据。
if(L=NULL) //内存不足,分配失败
return false;
L->next = MULL; //头节点之后暂时还没有节点
return true;
}
void test (){
LinkList L; //声明一个指向单链表的指针
//注意到此处并没有创建一个节点
//初始化一个空表
InitList(L);
//....后续代码。。。。。
}
//判断单链表是否为空
bool Empty(LinkLIst L){
if (L->next==NULL)
return true;
else
return false;
}
//or
bool Empty(LinkList L){
return (L->next==NULL);
}
二者区别:
- 不带头结点:写代码麻烦,第一个数据结点和后续数据结点的处理需要用不用的代码逻辑。
- 不带头节点:对空表和非空表的处理需要用不同的代码逻辑。
- 带头节点:写代码方便,对链表上的第一个位置的操作与其他位置的操作一样,不存在特殊处理。
总结
单链表是学习数据结构中,最先学习到的链式存储的一个逻辑结构。是学习双链表以及循环链表的基础。以后还要掌握单链表的建立,单链表的查找,单链表的插入,单链表的删除等操作。
注意,由于带头结点的单链表的代码更方便处理操作,一般而言,默认我们所用的单链表都是带头结点的单链表。