文件描述及头文件包含
/*******************************************************************
*
* 文件名称 : 单链表(非循环)的基本接口程序
* 文件作者 : [email protected]
* 创建日期 : 2024/05/07
* 文件功能 : 对单链表的增删改查功能的定义
* 注意事项 : None
*
* CopyRight (c) 2024 [email protected] All Right Reseverd
*
* *****************************************************************/
#include <stdlib.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdio.h>
定义数据类型,构造链表结点结构体
// 指的是单向链表中的结点有效数据类型,用户可以根据需要进行修改
typedef int DataType_t;
// 构造链表的结点,链表中所有结点的数据类型应该是相同的
typedef struct LinkedList
{
DataType_t data; // 结点的数据域
struct LinkedList *next; // 结点的指针域
} LList_t;
创建链表
/*******************************************************************
*
* 函数名称: LList_Create
* 函数功能: 创建一个空链表,空链表应该有一个头结点,对链表进行初始化
* 函数参数:
* 返回结果:
* @Head 返回创建成功后的头结点地址
* 注意事项: None
* 函数作者: [email protected]
* 创建日期: 2024/05/06
* 修改历史:
* 函数版本: V1.0
* *****************************************************************/
// 创建一个空链表,空链表应该有一个头结点,对链表进行初始化
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;
}
新结点创建
/*******************************************************************
*
* 函数名称: LList_NewNode
* 函数功能: 创建新的结点,并对新结点进行初始化(数据域 + 指针域)
* 函数参数:
* @data 传入需要新建结点数据域的值
* 返回结果:
* @New 返回创建成功后的新结点地址
* 注意事项: None
* 函数作者: [email protected]
* 创建日期: 2024/05/06
* 修改历史:
* 函数版本: V1.0
* *****************************************************************/
// 创建新的结点,并对新结点进行初始化(数据域 + 指针域)
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;
}
头插法插入新结点
/*******************************************************************
*
* 函数名称: LList_HeadInsert
* 函数功能: 向链表的头部插入新结点
* 函数参数:
* @Head 传入需要操作的链表的头结点
* @data 传入需要插入的数据
* 返回结果:
* @bool 返回插入是否成功
* 注意事项: None
* 函数作者: [email protected]
* 创建日期: 2024/05/06
* 修改历史:
* 函数版本: V1.0
* *****************************************************************/
// 头插
bool LList_HeadInsert(LList_t *Head, DataType_t data)
{
// 1.创建新的结点,并对新结点进行初始化
LList_t *New = LList_NewNode(data);
if (NULL == New)
{
printf("can not insert 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;
}
尾插法插入新结点
/*******************************************************************
*
* 函数名称: LList_TailInsert
* 函数功能: 向链表的尾部插入新结点
* 函数参数:
* @Head 传入需要操作的链表头结点地址
* @data 传入需要插入的结点的数据域的值
* 返回结果:
* @bool 返回插入是否成功
* 注意事项: None
* 函数作者: [email protected]
* 创建日期: 2024/05/06
* 修改历史:
* 函数版本: V1.0
* *****************************************************************/
bool LList_TailInsert(LList_t *Head, DataType_t data)
{
// 1.创建新的结点,并对新结点进行初始化
LList_t *New = LList_NewNode(data);
if (NULL == New)
{
printf("can not insert new node\n");
return false;
}
// 2.判断链表是否为空,如果为空,则直接插入即可
if (NULL == Head->next)
{
Head->next = New;
return true;
}
// 3.如果非空,向后遍历到尾结点进行插入操作
else
{
// 对链表的头文件的地址进行备份
LList_t *Phead = Head;
while (Phead->next)
{
Phead = Phead->next;
}
Phead->next = New;
}
return true;
}
目标结点后插入新结点
/*******************************************************************
*
* 函数名称: LList_DestInsert
* 函数功能: 向链表的指定节点后插入新结点
* 函数参数:
* @Head 传入需要操作的链表头结点地址
* @dest 传入目标结点的数据域的值
* @data 传入需要插入的结点的数据域的值
* 返回结果:
* @bool 返回插入是否成功
* 注意事项: None
* 函数作者: [email protected]
* 创建日期: 2024/05/06
* 修改历史:
* 函数版本: V1.0
* *****************************************************************/
bool LList_DestInsert(LList_t *Head, DataType_t dest, DataType_t data)
{
// 1.对链表的头文件的地址进行备份
LList_t *Phead = Head;
// 2.判断链表中是否有目标结点
while (Phead->next)
{
// 当存在目标结点时 停止循环
if (Phead->data == dest)
{
break;
}
else
{
Phead = Phead->next;
}
}
// 3.判断是否因为找到目标结点而停止循环
if (Phead->next == NULL && Phead->data != dest)
{
return false;
}
// 4.向该结点后插入新结点 创建新的结点,并对新结点进行初始化
LList_t *New = LList_NewNode(data);
New->next = Phead->next;
Phead->next = New;
return true;
}
头删法删除结点
/*******************************************************************
*
* 函数名称: LList_HeadDel
* 函数功能: 删除首结点
* 函数参数:
* @Head 传入需要操作的链表头结点地址
* 返回结果:
* 注意事项: None
* 函数作者: [email protected]
* 创建日期: 2024/05/06
* 修改历史:
* 函数版本: V1.0
* *****************************************************************/
bool LList_HeadDel(LList_t *Head)
{
// 1.判断链表是否为空
if (Head->next == NULL)
{
return false;
}
else
{
// 2.对链表的首结点的地址进行备份
LList_t *Phead = Head->next;
// 3.删除首结点
Head->next = Phead->next; // 将头结点指向首结点的下一个结点
Phead->next = NULL; // 将原首结点的next指针指向NULL
free(Phead); // 释放原首结点的空间
return true;
}
}
尾删法删除结点
/*******************************************************************
*
* 函数名称: LList_HeadDel
* 函数功能: 删除尾结点
* 函数参数:
* @Head 传入需要操作的链表头结点地址
* 返回结果:
* 注意事项: None
* 函数作者: [email protected]
* 创建日期: 2024/05/06
* 修改历史:
* 函数版本: V1.0
* *****************************************************************/
bool LList_TailDel(LList_t *Head)
{
// 1.判断链表是否为空
if (Head->next == NULL)
{
return false;
}
else
{
// 2.对链表的首结点及头结点的地址进行备份
LList_t *temp = Head;
LList_t *Phead = Head->next;
// 3.遍历查找尾结点地址
while (Phead->next)
{
temp = temp->next;
Phead = Phead->next;
}
// 4.删除尾结点
temp->next = NULL;
free(Phead);
return true;
}
}
删除指定节点
/*******************************************************************
*
* 函数名称: LList_DestDel
* 函数功能: 删除指定结点
* 函数参数:
* @Head 传入需要操作的链表头结点地址
* @dest 传入需要删除的目标结点
* 返回结果:
* 注意事项: None
* 函数作者: [email protected]
* 创建日期: 2024/05/06
* 修改历史:
* 函数版本: V1.0
* *****************************************************************/
bool LList_DestDel(LList_t *Head, DataType_t dest)
{
// 1.判断链表是否为空
if (Head->next == NULL)
{
return false;
}
else
{
// 2.对链表的首结点及头结点的地址进行备份
LList_t *temp = Head;
LList_t *Phead = Head->next;
// 3.遍历链表寻找目标结点
while (Phead)
{
if (Phead->data == dest) // 当前结点数据域的值与目标值相同时 跳出循环
{
break;
}
temp = temp->next; // 不同时 指针向后继续遍历
Phead = Phead->next;
}
// 4.判断是否因为找到尾结点而结束
if (Phead->next == NULL && Phead->data != dest)
{
return false;
}
else
{
temp->next = Phead->next; // 将目标结点直接前驱的next指针指向目标结点直接后继的地址
Phead->next = NULL; // 将目标节点的next指针指向NULL
free(Phead); // 释放目标结点的内存
return true;
}
}
}
遍历打印所有结点
/*******************************************************************
*
* 函数名称: LList_Print
* 函数功能: 遍历打印所有结点
* 函数参数:
* @Head 传入需要操作的链表头结点地址
* 返回结果:
* 注意事项: None
* 函数作者: [email protected]
* 创建日期: 2024/05/06
* 修改历史:
* 函数版本: V1.0
* *****************************************************************/
// 遍历
void LList_Print(LList_t *Head)
{
// 对链表的头文件的地址进行备份
LList_t *Phead = Head;
// 首结点
while (Phead->next)
{
// 把头的直接后继作为新的头结点
Phead = Phead->next;
// 输出头结点的直接后继的数据域
printf("data = %d\n", Phead->data);
}
}
测试主函数
/*******************************************************************
*
* 函数名称: main
* 函数功能: 主函数 用于测试自定义函数功能是否正确
* 函数参数:
* 返回结果:
* 注意事项: None
* 函数作者: [email protected]
* 创建日期: 2024/05/06
* 修改历史:
* 函数版本: V1.0
* *****************************************************************/
int main()
{
// 新建空链表
LList_t *head = LList_Create();
// 尾插法插入数据10 15
LList_TailInsert(head, 10);
LList_TailInsert(head, 15);
LList_Print(head);
printf("\n");
// 头插法插入数据30 25 20 10 15
LList_HeadInsert(head, 20);
LList_HeadInsert(head, 25);
LList_HeadInsert(head, 30);
LList_Print(head);
printf("\n");
// 指定目标后插入数据30 25 20 5 10 15
LList_DestInsert(head, 20, 5);
LList_Print(head);
printf("\n");
// 头删25 20 5 10 15
LList_HeadDel(head);
LList_Print(head);
printf("\n");
// 尾删25 20 5 10
LList_TailDel(head);
LList_Print(head);
printf("\n");
// 指定删除目标结点25 5 10
LList_DestDel(head, 20);
LList_Print(head);
printf("\n");
return 0;
}