C语言实现单链表

一、什么是单链表

单链表是 C 语言中最基础、最重要的数据结构之一,非常适合用来练习结构体、指针和动态内存管理。

单链表的每个结点由两部分组成:数据域和指针域。数据域用于存储实际数据,指针域用于保存下一个结点的地址,多个结点通过指针依次连接,最终以 NULL 结尾形成完整链表。

二、单链表头文件设计(SList.h)

在实现单链表之前,通常先定义好结点结构和对外提供的操作接口,代码如下。

java 复制代码
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int SLTDataType;

typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

void SLTPrint(SLTNode* phead);
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(SLTNode** pphead, SLTDataType x);

void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);

SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
void SLTErase(SLTNode** pphead, SLTNode* pos);
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
void SLTEraseAfter(SLTNode* pos);

SLTDataType 使用 typedef 定义为 int,方便后续统一修改链表中存储的数据类型。

SLTNode 结构体表示单链表的结点,其中 data 为数据域,next 为指针域,指向下一个结点。多个结点通过 next 串联形成完整链表。

SLTPrint 用于遍历并打印链表,主要用于调试和测试阶段查看链表结构是否正确。

SLTPushBackSLTPushFront 分别实现尾插和头插操作,由于它们需要修改头指针,因此参数采用二级指针 SLTNode** pphead

SLTPopBackSLTPopFront 用于删除链表尾结点和头结点,同样需要修改头指针,所以也使用二级指针作为参数。

SLTFind 用于在链表中查找指定值,若找到则返回该结点指针,否则返回 NULL,便于后续进行插入或删除等操作。

SLTInsert 用于在指定结点 pos 前插入新结点,如果 pos 为头结点,则等价于头插操作;SLTErase 用于删除指定结点 pos

SLTInsertAfterSLTEraseAfter 分别表示在指定结点之后插入新结点,以及删除指定结点之后的结点。这两个操作不需要修改头指针,因此参数只需使用一级指针。

三、单链表实现(SList.c)

在前文中已经完成了单链表头文件的接口设计,下面给出对应的 .c 文件实现,通过这些基础操作,可以完整实现单链表的增删查改功能。

在实现功能前,我们增加一个函数SLTNode* BuySLTNode(SLTDataType x),来动态申请内存。该函数使用 malloc 申请空间,并对结点数据域和指针域进行初始化。如果内存申请失败,则打印错误信息并返回。完整代码如下。

java 复制代码
#include"SList.h"

SLTNode* BuySLTNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

void SLTPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuySLTNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* cur = *pphead;
		while (cur->next != NULL)
		{
			cur = cur->next;
		}
		cur->next = newnode;
	}
}

void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuySLTNode(x);
		newnode->next = *pphead;
		*pphead = newnode;
}

void SLTPopBack(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	SLTNode* cur = *pphead;
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		while (cur->next->next != NULL)
		{
			cur = cur->next;
		}
		free(cur->next);
		cur->next = NULL;
	}
}

void SLTPopFront(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	SLTNode* cur = *pphead;
	*pphead = cur->next;
	free(cur);
	cur = NULL;
}

SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{

	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
			return cur;
		cur = cur->next;
	}
	return NULL;

}

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);
	if (pos == *pphead)
	{
		SLTPushFront(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		SLTNode* newnode = BuySLTNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}

void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);
	if (pos == *pphead)
	{
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* cur = *pphead;
		while (cur->next != pos)
		{
			cur = cur->next;
		}
		cur->next = pos->next;
		free(pos);
	}
}

void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* newnode = BuySLTNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

void SLTEraseAfter(SLTNode* pos)
{
	assert(pos);
	assert(pos->next);
	SLTNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}

四、测试代码(Test.c)

java 复制代码
#include"SList.h"

int main()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);

	SLTPrint(plist);

	SLTPushFront(&plist, 6);
	SLTPushFront(&plist, 7);
	SLTPushFront(&plist, 8);
	SLTPrint(plist);

	SLTPopBack(&plist);
	SLTPopFront(&plist);
	SLTPrint(plist);

	// 值为2那个节点  *2
	SLTNode* ret = SLTFind(plist, 2);
	ret->data *= 2;
	SLTPrint(plist);

	SLTInsert(&plist, ret, 20);
	SLTPrint(plist);

	/*SLTErase(&plist, ret);
	ret = NULL;
	SLTPrint(plist);*/


	SLTInsertAfter(ret, 666);
	SLTPrint(plist);

	SLTEraseAfter(ret);
	SLTPrint(plist);


}

结果如下。

相关推荐
Wect16 小时前
LeetCode 130. 被围绕的区域:两种解法详解(BFS/DFS)
前端·算法·typescript
NAGNIP1 天前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
颜酱1 天前
单调栈:从模板到实战
javascript·后端·算法
CoovallyAIHub2 天前
仿生学突破:SILD模型如何让无人机在电力线迷宫中发现“隐形威胁”
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
从春晚机器人到零样本革命:YOLO26-Pose姿态估计实战指南
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
Le-DETR:省80%预训练数据,这个实时检测Transformer刷新SOTA|Georgia Tech & 北交大
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
强化学习凭什么比监督学习更聪明?RL的“聪明”并非来自算法,而是因为它学会了“挑食”
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
YOLO-IOD深度解析:打破实时增量目标检测的三重知识冲突
深度学习·算法·计算机视觉
祈安_2 天前
C语言内存函数
c语言·后端
NAGNIP2 天前
轻松搞懂全连接神经网络结构!
人工智能·算法·面试