单向链表
/******************************************************************************
*
*   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 链表非空
/******************************************************************************
*
*   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 链表非空
/******************************************************************************
*
*   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 链表非空
/******************************************************************************
*
*   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;
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇