/******************************************************************************
*
* file name : LinkedDist
* author : Wzy
* data : 2025/12/05
* function : 构造记录链表的各项参数的结构体,并且完成对链表中的元素的增加和删除和访问
* note : None
*
* copyRight (c) 2025 17630246607@163.com All Right Reseverd
* ****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdlib.h>
//指的是单向链表中的结点的数据类型,用户可以根据需要进行修改
typedef int DataType_t;
//构造链表结点,链表中所有结点的数据类型应该是相同的,每个结点有两部分组成,第一部分是自身的数据,第二部分是直接后继数据的地址
typedef struct LinkedList
{
DataType_t data; //结点的数据域 //自身的数据
struct LinkedList *next; //结点的指针域 //直接后继数据的地址
}LList_t;
- 带头结点的链表

/******************************************************************************
*
* func name : LList_Create
* function : 创建一个空链表,空链表应该有一个头结点,对链表进行初始化
* argument : None
* retval : 返回头结点地址
* author : Wzy
* date : 2025/12/05
* note : None
*
* ****************************************************************************/
//创建一个空链表,空链表应该有一个头结点,对链表进行初始化
LList_t * LList_Create(void)
{
//1.创建一个头结点并对头结点申请内存
LList_t *Head = (LList_t *)calloc(1,sizeof(LList_t));
if(NULL == Head)
{
perror("calloc memory for head is failed");
exit(-1); //程序异常终止
}
//2.对头结点进行初始化,头结点是不存储有效内容
Head->next = NULL;
//3.把头结点的地址返回
return Head;
}
/******************************************************************************
*
* func name : LList_NewNode
* function : 创建新的结点,并对新结点进行初始化(数据域 + 指针域)
* argument :
* @DataType_t data:结点的数据域,即自身要存储的数据
* retval : 返回新结点的地址
* author : Wzy
* date : 2025/12/06
* note : None
*
* ****************************************************************************/
//创建新的结点,并对新结点进行初始化(数据域 + 指针域)
LList_t * LList_NewNode(DataType_t data)
{
//1.创建一个新的结点并对新结点申请内存
LList_t *New = (LList_t *)calloc(1,sizeof(LList_t));
if(NULL == New)
{
perror("calloc memory for NewNode is failed");
return NULL;
}
//2.对新结点的数据域和指针域进行初始化
New->data = data;
New->next = NULL;
return New;
}
- 向链表中插入新结点(头插:把插入的那个新结点变成首结点)
- 链表为空 or 链表非空
-2.png)
.png)
/******************************************************************************
*
* func name : LList_HeadInsert
* function : 在一个链表中头插一个新的结点
* argument :
* @ Head: 一个指向头结点的指针。
* @DataType_t data:结点的数据域,即自身要存储的数据
* retval : 成功插入返回true,插入失败返回false
* author : Wzy
* date : 2025/12/06
* note : None
*
* ****************************************************************************/
//头插
bool LList_HeadInsert(LList_t * Head,DataType_t data)
{
//1.调用LList_NewNode函数,创建新的结点,并对新结点进行初始化
LList_t *New = LList_NewNode(data); //LList_NewNode函数的返回值是指向新结点的地址,所以需要一个指针变量来接收这个地址
if(NULL == New)
{
printf("cat not issert New Node\n");
return false;
}
//2.判断链表是否为空,如果为空,则直接插入
if(NULL == Head->next)
{
Head->next = New;
return true;
}
//3.如果链表为非空,则把新结点插入到链表的头部
New->next = Head->next;
Head->next = New;
return true;
}
头插测试结果


- 向链表中插入新结点(尾插:把插入的那个新结点变成尾结点)
- 链表为空 or 链表非空
-1024x214.png)
/******************************************************************************
*
* func name : LList_TailInsert
* function : 在一个链表中尾部插一个新的结点
* argument :
* @ Head: 一个指向头结点的指针。
* @DataType_t data:结点的数据域,即自身要存储的数据
* retval : 成功插入返回true,插入失败返回false
* author : Wzy
* date : 2025/12/06
* note : 为了防止最后头结点的地址找不到,所以需要对链表的头结点进行备份
*
* ****************************************************************************/
//尾插
bool LList_TailInsert(LList_t * Head,DataType_t data)
{
//对链表的头结点的地址进行备份
LList_t * Phead = Head;
//1.调用LList_NewNode函数,创建新的结点,并对新结点进行初始化
LList_t *New = LList_NewNode(data); //LList_NewNode函数的返回值是指向新结点的地址,所以需要一个指针变量来接收这个地址
if(NULL == New)
{
printf("cat not issert New Node\n");
return false;
}
//2.判断链表是否为空,如果为空,则直接插入
if(NULL == Head->next)
{
Head->next = New;
return true;
}
//3.如果链表为非空,则把新结点插入到链表的尾部
while(Phead->next == NULL)
{
//把头结点的直接后继结点作为新的头结点
Phead = Phead->next;
}
Phead->next = New;
return true;
}
尾插测结果


- 向链表中插入新结点(指定插(中间插):把新结点插入到中间)
- 链表为空 or 链表非空

/******************************************************************************
*
* func name : LList_DestInsert
* function : 在一个链表中指定地方插一个新的结点
* argument :
* @ Head: 一个指向头结点的指针。
* @DataType_t dest:目标结点的数据域,要在这个目标结点的后面插入新结点
* @DataType_t data:结点的数据域,即自身要存储的数据
* retval : 成功插入返回true,插入失败返回false
* author : Wzy
* date : 2025/12/06
* note : 先利用目标结点的数据域遍历查找整个链表,找到目标结点的指针域,
* 然后把新结点的指针域指向目标结点的直接后继的那个结点,然后把目标结点的指针域指向新结点
*
* ****************************************************************************/
//指定位置插入
bool LList_DestInsert(LList_t * Head,DataType_t dest,DataType_t data)
{
//对链表的头结点的地址进行备份
LList_t * Phead = Head->next;
//1.调用LList_NewNode函数,创建新的结点,并对新结点进行初始化
LList_t *New = LList_NewNode(data); //LList_NewNode函数的返回值是指向新结点的地址,所以需要一个指针变量来接收这个地址
if(NULL == New)
{
printf("cat not issert New Node\n");
return false;
}
//2.判断链表是否为空,如果为空,则直接插入
if(NULL == Head->next)
{
Head->next = New;
return true;
}
//3.遍历链表,目的是找到目标结点
while(Phead != NULL && dest != Phead->data)
{
Phead = Phead->next;
}
if (NULL == Phead)
{
return false;
}
//4.找到目标结点,则把新结点加入到目标结点的后面
New->next = Phead->next;
Phead->next = New;
}
指定位置插入测试结果


遍历整个单链表
/******************************************************************************
*
* func name : LList_Print
* function : 遍历整个链表
* argument :
* @Head: 一个指向头结点的指针。
* retval : None
* author : Wzy
* date : 2025/12/06
* note : None
*
* ****************************************************************************/
void LList_Print(LList_t * Head)
{
//对链表的头结点的地址进行备份
LList_t * Phead = Head;
while(Phead->next == NULL)
{
//把头结点的直接后继结点作为新的头结点
Phead = Phead->next;
//输出头结点的直接后继结点的数据域
pfintf("data = %d",Phead->data);
}
}
- 删除链表中的结点(头删)
- 链表为空 or 链表非空
-1.png)
-1024x221.png)
/******************************************************************************
*
* func name : HeadDel
* function : 删除首结点
* argument :
* @Head: 一个指向头结点的指针。
* retval : 删除成功返回true,删除失败返回false
* author : Wzy
* date : 2025/12/06
* note : None
*
* ****************************************************************************/
//头删
bool LList_HeadDel(LList_t *Head)
{
//对链表的头结点的地址进行备份
LList_t * Phead = Head;
//2.判断链表是否为空,如果为空,则无法删除,直接退出
if(NULL == Head->next)
{
return false;
}
//3.保存首结点地址(避免后续指针修改后丢失)
LList_t *del_node = Phead->next;
//4.链表为非空,则直接删除首结点
Head->next = del_node->next;
del_node->next = NULL;
free(del_node);
return true;
}
测试头删结果


- 删除单链表中最小值结点
/******************************************************************************
*
* func name : LList_DelMin
* function : 删除单链表中最小值结点
* argument :
* @Head: 一个指向头结点的指针。
* retval : None
* author : Wzy
* date : 2025/12/07
* note : None
*
* ****************************************************************************/
void LList_DelMin(LList_t * Head)
{
// 1. 先判断链表是否为空,为空则直接返回(避免空指针访问)
if (NULL == Head->next)
{
printf("链表为空,无法删除最小值结点\n");
return;
}
LList_t * min_prev = Head; //记录最小值结点的直接前驱地址
LList_t * min = Head->next; //记录最小值结点的地址,并且初始化为首结点的地址
LList_t * Phead = Head->next; //记录当前结点的地址,并且初始化为首结点的地址
LList_t * Phead_prev = Head; //记录当前结点的直接前驱地址,并且初始化为头结点
//1.遍历链表,目的是找到最小值结点
while(Phead->next)
{
//比较链表中结点的数据域大小
if(min->data > Phead->next->data)
{
min = Phead->next;
min_prev = Phead;
}
//如果发现当前结点的数据域不大于当前结点的直接后继的数据域,则向后遍历
Phead_prev = Phead;
Phead = Phead->next;
}
//2.删除当前的最小值结点,前提是让最小值结点的直接前驱指向最小值结点的直接后继
min_prev->next = min->next;
//3.释放最小值结点的内存
min->next = NULL;
free(min);
}
测试删除单链表中最小值结点结果


main函数
int main(void)
{
// 1. 创建空链表(头结点)
LList_t *LList = LList_Create();
if (NULL == LList)
{
printf("链表创建失败!\n");
return -1;
}
printf("=== 初始链表创建成功 ===\n");
// 2. 测试头插函数 LList_HeadInsert
printf("\n=== 测试头插操作 ===\n");
LList_HeadInsert(LList, 10); // 头插 10
LList_HeadInsert(LList, 20); // 头插 20
LList_HeadInsert(LList, 30); // 头插 30
printf("头插 30/20/10 后链表内容:\n");
LList_Print(LList); // 预期输出:data = 30 data = 20 data = 10
printf("\n");
// 3. 测试尾插函数 LList_TailInsert
printf("\n=== 测试尾插操作 ===\n");
LList_TailInsert(LList, 5); // 尾插 5
LList_TailInsert(LList, 1); // 尾插 1(用于后续测试删除最小值)
printf("尾插 5/1 后链表内容:\n");
LList_Print(LList); // 预期输出:data = 30 data = 20 data = 10 data = 5 data = 1
printf("\n");
// 4. 测试指定位置插入函数 LList_DestInsert
printf("\n=== 测试指定位置插入操作 ===\n");
// 在数据为 10 的结点后插入 15
bool insert_ret = LList_DestInsert(LList, 10, 15);
if (insert_ret)
{
printf("在 10 后插入 15 成功,链表内容:\n");
LList_Print(LList); // 预期:30 20 10 15 5 1
}
else
{
printf("在 10 后插入 15 失败!\n");
}
// 测试目标结点不存在的情况
insert_ret = LList_DestInsert(LList, 99, 88);
if (!insert_ret)
{
printf("\n目标结点 99 不存在,插入 88 失败(符合预期)\n");
}
printf("\n");
// 5. 测试头删函数 LList_HeadDel
printf("=== 测试头删操作 ===\n");
bool del_ret = LList_HeadDel(LList);
if (del_ret)
{
printf("头删一次后链表内容:\n");
LList_Print(LList); // 预期:20 10 15 5 1
}
else
{
printf("头删失败!\n");
}
// 多次头删,验证边界
LList_HeadDel(LList);
printf("\n再头删一次后链表内容:\n");
LList_Print(LList); // 预期:10 15 5 1
printf("\n");
// 6. 测试删除最小值结点函数 LList_DelMin
printf("=== 测试删除最小值结点操作 ===\n");
printf("删除最小值前链表内容:\n");
LList_Print(LList); // 当前:10 15 5 1
LList_DelMin(LList);
printf("\n删除最小值(1)后链表内容:\n");
LList_Print(LList); // 预期:10 15 5
printf("\n");
// 7. 测试空链表头删(边界场景)
printf("=== 测试空链表头删(边界)===\n");
// 先清空链表
while (LList_HeadDel(LList));
printf("清空链表后,再次头删:\n");
del_ret = LList_HeadDel(LList);
if (!del_ret)
{
printf("头删失败(空链表,符合预期)\n");
}
// 8. 测试空链表删除最小值(边界场景)
printf("\n=== 测试空链表删除最小值(边界)===\n");
LList_DelMin(LList); // 空链表调用,需确保不崩溃
printf("空链表调用 LList_DelMin 完成(无崩溃即符合预期)\n");
return 0;
}
