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);


}

结果如下。

相关推荐
mabing9933 小时前
Qt QMessageBox、QDialogButtonBox中英文翻译动态切换
开发语言·qt
Sagittarius_A*3 小时前
【RCELABS】Level 17~18 —— PHP命令执行函数与环境变量注入
开发语言·安全·web安全·靶场·php·rce
冻柠檬飞冰走茶3 小时前
PTA基础编程题目集 7-19 支票面额(C语言实现)
c语言·开发语言·数据结构·算法
破碎的南瓜3 小时前
靶场中使用到的php函数以及每种漏洞的防御方式
开发语言·php
你怎么知道我是队长3 小时前
计算机虚拟存储管理与页面置换算法详解
服务器·网络·算法
罗西的思考4 小时前
【Agentic RL / 强化学习 / OPD】OpenClaw-RL 源码阅读笔记 — (14)— Teacher
人工智能·笔记·深度学习·算法·机器学习
谁看我谁 狼家二丫4 小时前
局域网文件共享实战:从“账户被禁用”到成功互传文件
开发语言·php
塔能物联运维4 小时前
塔能两相液冷:600W/cm²极限能力,为下一代AI芯片预留充足散热余量
人工智能·算法·两相液冷
醉城夜风~4 小时前
手撕 C++ string:从零实现一个简易字符串类
开发语言·c++·算法
秋田君4 小时前
QT_QT布局常用类QSplitter窗口分割类与QDockWidget窗口停靠类
开发语言·数据库·qt