写有“售后”的博客:一定回复评论和私信,答疑解惑到底,认真探讨大家的疑问,欢迎大家多多指出批评与建议。
参考书籍:《大话数据结构》,《数据结构(C语言版)》
目录
一、单链表的简介
单链表由一节节“车厢”连接而成,每节车厢被称为一个节点/结点。节点分为两部分,数据部和指针部,上一个节点的指针部存储下一个节点的地址,从而实现了拿到地址找到你家你别想跑的链接效果,我们称为:上一个节点的指针指向下一个节点,最后一个节点的指针指向空。
如果我们希望plist“指向”第⼆个节点时,只需要修改plist保存的内容为0x0012FFA0。
二、单链表为什么要用到二级指针
已经知道的朋友可以跳过二,还不知道的初学者需要先去看我的另一篇文章数据结构:单链表(不带头链表)为什么要用到二级指针
三、单链表的基本操作
1. 相关的头文件
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#pragma once防止头文件被重复包含
<assert.h>是assert断言的头文件
<stdlib.h>是动态内存空间开辟函数以及free释放的头文件
2. 定义单链表结构体(定义节点)
typedef int SLTDataType;
typedef struct SLTNode
{
SLTDataType data;
struct SLTNode* next;
}SLTNode;
data是节点中存放的数据。
节点中存放的数据可能变为各种类型,我们为了修改方便使用typedef 类型 STDataType;
next是指向下一个节点的指针。
3. 手动制造单链表
void test01()
{
SLTNode* Node1=(SLTNode*)malloc(sizeof(SLTNode));
SLTNode* Node2 = (SLTNode*)malloc(sizeof(SLTNode));
SLTNode* Node3 = (SLTNode*)malloc(sizeof(SLTNode));
SLTNode* Node4 = (SLTNode*)malloc(sizeof(SLTNode));
Node1->next = Node2;
Node2->next = Node3;
Node3->next = Node4;
Node4->next = NULL;
Node1->data = 1;
Node2->data = 2;
Node3->data = 3;
Node4->data = 4;
}
前四行,动态开辟了四个节点的空间。
Node1是指针,存储了第一个节点的地址,Node1根据储存的地址指向第一个节点。Node2是指针,存储了第二个节点的地址,Node2根据储存的地址指向第二个节点。Node3、4同理指向第三、四个节点。
中间四行,第一行,把Node2的值传给Node1指向的节点的next指针,next指针此时存储了第二个节点的地址,因此第一个节点的next指针指向第二个节点。同理,第二个节点的next指针指向第三个节点;第三个节点的next指针指向第四个节点;第四个节点的next指针指向NULL;
后四行,为每个节点的data赋值。
4. 单链表的打印
void SLTPrint(SLTNode* phead)
{
SLTNode* pcur = phead;
while (pcur != NULL)
{
printf("%d->", pcur->data);
pcur = pcur->next;
}
printf("NULL\n");
}
5. 创建节点
SLTNode* SLTBuyNode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if(newnode==NULL)
{
perror("malloc");
exit(1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
最后返回该节点的地址
6. 尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* newnode = SLTBuyNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
SLTNode* pcur = *pphead;
while (pcur->next != NULL)
{
pcur = pcur->next;
}
pcur->next = newnode;
}
}
7. 头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* newnode = SLTBuyNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
8. 尾删
void SLTPopBack(SLTNode** pphead)
{
assert(pphead && *pphead);
if ((*pphead)->next==NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* prve = *pphead;
SLTNode* pcur = *pphead;
while (pcur->next != NULL)
{
prve = pcur;
pcur = pcur->next;
}
prve->next = NULL;
free(pcur);
pcur = NULL;
}
}
9.头删
void SLTPopFront(SLTNode** pphead)
{
assert(pphead && *pphead);
SLTNode* pcur = (*pphead)->next;
free(*pphead);
*pphead = pcur;
pcur = NULL;
}
10. 寻找节点
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
SLTNode* pcur = phead;
while (pcur != NULL)
{
if (pcur->data == x)
{
return pcur;
}
pcur = pcur->next;
}
return NULL;
}
11. 在指定位置之前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
assert(pphead&&pos);
SLTNode* newnode = SLTBuyNode(x);
if (*pphead == pos)
{
SLTPushFront(pphead, x);
}
else
{
SLTNode* pcur = *pphead;
while (pcur->next != pos)
{
pcur = pcur->next;
}
pcur->next = newnode;
newnode->next = pos;
}
}
12. 在指定位置之后插入
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
assert(pos);
SLTNode* newnode = SLTBuyNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
在指定位置之后插入不会改变头结点,也用不到头结点,因此不用接收头结点的地址。
13. 删除指定位置
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead && pos);
if (*pphead == pos)
{
SLTPopFront(pphead);
}
else
{
SLTNode* prve = *pphead;
while (prve->next != pos)
{
prve = prve->next;
}
prve->next = pos->next;
free(pos);
pos = NULL;
}
14. 删除指定位置的下一个位置
void SLTEraseAfter(SLTNode* pos)
{
assert(pos&&pos->next);
SLTNode* del = pos->next;
pos->next = del->next;
free(del);
del = NULL;
}
在指定位置之后删除不会改变头结点,也用不到头结点,因此不用接收头结点的地址。
15. 单链表的销毁
void SLTDestroy(SLTNode** pphead)
{
assert(pphead);
SLTNode* prve = *pphead;
SLTNode* pcur = prve;
while (pcur != NULL)
{
pcur = pcur->next;
free(prve);
prve = pcur;
}
*pphead = NULL;
}
完~
期待大家的评论和私信!