链表
链表作为一种线性表,相比顺序表来说,解决了不必须顺序存储的问题,由于其结构的特殊性,使其插入元素时变的很方便,可以达到
单链表的存储
链表中每个数据的存储由两部分组成,分别是数据元素本身,其所在的区域称为数据域;指向直接后继元素的指针,所在的区域称为指针域。这样一个整体叫做一个结点。
也就是链表的实际存储是由一个个结点连接而成。例如,用单链表存储
一个完整的链表还应该包括头指针,头结点。头指针永远指向链表第一个结点的位置。它的作用就是方便且快速的找到链表并使用表中的数据;头结点通常是一个空结点,这个可有可无,对不同的问题不同对待即可。
单链表的定义一般都是由结构体实现的,例如下面形式:
typedef struct Link{
char elem; //数据域
struct Link * next; //指针域,指向直接后继元素
}link; //link为节点名,每个节点都是一个 link 结构体
由于每一个结点的指针域直接指向下一个结点的数据域,即代表下一个结点数据域的地址,因此这里采用结构体里面嵌套结构体结构体来实现。
单链表的使用
插入
向链表中插入数据,插入位置和顺序表一样,有三种:表头,表中,表尾。但链表的插入相比顺序表就方便多了,只需要改变指针域的指向就可以实现。步骤有两个:
- 首先需要把插入数据的指针指向插入位置下一个结点的数据域
- 然后把插入位置上一个结点的指针指向插入数据的数据域。
具体如下图演示
删除
删除就更简单了,只要确定要删除的结点,把该结点前一个结点的指针域指向该结点下一个结点的数据域就行。 这样做虽说已经达到删除的目的了,但有个小问题就是,虽说已经删除元素,但该存储空间还未释放,因此,还要释放存储空间。
查找
查找,我们通常采用依次遍历对比的方法:从表头依次遍历表中结点,用被查找元素与各节点数据域中存储的数据元素进行比对,直至比对成功或遍历至链表最末端的 NULL(代表着查找失败)。 其他的都没有放代码,但查找还没找到一种很好的表述方式,就先把代码放上吧。
//p为原链表,elem表示被查找元素、
int select(link * p,int elem){
link * t=p;
//由于头节点的存在,因此while中的判断为t->next
while (t->next) {
t=t->next;
if (t->elem==elem) {
return elem; //查找成功,返回该元素
}
}
//程序执行至此处,表示查找失败
printf("查找失败");
return -1;
}
双向链表的存储
单链表结构简单,访问方便,但他有一个很大的缺点就是只能访问当前结点的下一个结点,访问不了当前结点的上一个结点,如果非要访问,就必须要从头开始访问。因此,为了解决这一问题,双链表就应用而生了,双链表的结点结构如下图示:
例如,用双链表存储数据
双向链表的定义如下:
typedef struct S_link{
struct S_line * prior; //指向直接前趋
int data;
struct S_line * next; //指向直接后继
}s_line;
双向链表的使用
插入
下图描述了双向链表三种位置的插入情况
删除
查找
双向链表和单链表一样,都仅有一个头指针。因此,双链表查找指定元素的实现同单链表类似,都是从表头依次遍历表中元素。
总结
本文采用图形化的方式详细地介绍了链表的结构以及各种操作,让大家对链表的理解变得更容易,为以后的写代码打下了良好的理论基础。
欢迎关注本人公众号