链表是数据结构中比较重要的一种结构,起初我在学习的时候也糊涂了好久,后来呢,一直独自写了好几遍才渐渐有了感觉。我的经验是必须要深入到内存中去学习,或者说是深刻理解一个链表从创建到销毁的过程中内存的变化。
单链表有两种,一种是头结点有数据的,一种是头结点不含数据的,其实两者没有什么大差别,本文演示的是头结点不含数据的单链表。
链表是什么样的呢?首先,我们要知道节点是什么?
节点在内存中就是我们开辟出的一小块空间,它由两小块组成,一部分是数据域,一部分是指针域。
typedef int Linkdata;
typedef struct LinkList//链表
{
Linkdata data;//数据域,存放当前节点的信息
struct LinkList* next;//指针域,存放下一个节点的地址
}LL;
而链表呢?
因为在开辟空间的时候是随机在内存的某个位置开辟出一块空间,要想要这些独立的空间联系在一起,就需要一条链。链表就是将这些一个个节点(一块块小空间)连接在一起形成一条链,我们只需要知道链头,就可以找到整条链的所有信息。实际上,就是将这些内存中无序排列的空间串成一条链,就好像火车一样,车头连接着一节节车厢,直到车尾。
-
这里链表的头结点不存放数据,第二个节点开始存放有效数据,每一个节点的指针域都存放着下一个节点的地址(每一个节点都指向它的下一个节点)。
-
head指向头结点,(head是一个指针,称为头指针,存放着头结点的地址)。
-
最后一个节点的指针为空(NULL),也可理解为指向空。
链表的基础操作:
初始化
建立一个链表,首先要对其初始化,初始化下的链表不含有效数据,头结点即尾结点,指针域指向空。两种理解方式:第一种比较好理解吧,实际上第二种更符合链表在内存中的状态。
LL* Linkinit(LL* la)//链表初始化
{
la = (LL*)malloc(sizeof(LL));
if (la == NULL)//malloc函数申请空间失败就会返回空值
{
exit(0);//表示出错,申请空间失败,程序正常退出
}
la->next = NULL;
}
增加
头插法
头插法表是将一个新节点插入到链表的头部,实际上是头结点后面,因为头结点不含数据。分两步:先连后断
- 对头指针直接进行操作会移动头指针位置,从而失去链表表头的位置,遗失链表信息。因此,每进入一个函数都要首先创建一个临时头指针代替头指针进行之后的一系列操作
void addheadLink(LL* la, Linkdata x)//la表示头指针,实际上就是head,x为数据
{
LL* node=la;//每进入一个操作函数都要首先进行这一步,目的是不直接对头指针进行操作,否则会移动头指针的位置,链表的位置将会遗失
//*创建待插入节点*
LL* temp;
temp