C语言实现链表(无头+单向+非循环)

本文详细介绍了链表的基础知识,包括无头单向非循环链表的实现,如创建、打印、尾插、尾删、头插和头删等操作。这些操作是数据结构基础的重要组成部分,常在笔试面试中出现,并且是构建更复杂数据结构如哈希桶和图的邻接表的基础。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 链表的简单介绍

链表可根据单向双向、有头无头、循环以及非循环来分类,组合起来一共有8种链表结构。其中最常用的是无头单向非循环链表以及带头双向循环链表。今天实现的是无头单向非循环链表,它的特点是结构简单,一般不会单独用来存数据,实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等。在笔试面试中也出现的很多。而带头双向循环链表的结构最复杂,一般用在单独存储数据。

链表的实现

 接口如下:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int SLTDataType;

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

SLTNode* BuySLTNode(SLTDataType x) //动态申请一个结点
SLTNode* CreateSLT(int n) //创建一个链表
void SLTPrint(SLTNode* phead) //单链表打印
void SLTPushBack(SLTNode** pphead, SLTDataType x) //单链表尾插
void SLTPopBack(SLTNode** pphead) //单链表尾删
void SLTPushFront(SLTNode** pphead, SLTDataType x) //单链表头插
void SLTPopFront(SLTNode** pphead) //单链表头删

 动态申请一个结点

注意只能用malloc而不是直接定义,因为malloc申请的内存是在堆区上的,才不会出现出了函数就销毁了的情况。

SLTNode* BuySLTNode(SLTDataType x)
{
	SLTNode* tmp = (SLTNode*)malloc(sizeof(SLTNode));
	if (tmp == NULL)
	{
		perror("malooc fail");
		exit(-1);
	}
	else
	{
		tmp->data = x;
		tmp->next = NULL;
	}
	return tmp;
}

创建一个单链表

SLTNode* CreateSLT(int n)
{
	SLTNode* phead = NULL;
	SLTNode* ptail = NULL;

	for (int i = 0;i < n;i++)
	{
		SLTNode* newnode = BuySLTNode(i);//为了方便就设置为i
		if (phead == NULL)
		{
			ptail = phead = newnode;
		}
		else
		{
			ptail->next = newnode;
			ptail = newnode;
		}
	}
	return phead;
}

打印单链表

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

单链表尾插

注意传参时使用的是二级指针。原因是形参的改变不能影响实参,因此如果我们要改变实参的值应该通过解引用指向他的指针来改变。而在这里,我们需要改变的实参是一个一级指针,因此需要使用二级指针。

void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuySLTNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* tail = *pphead;
        //找尾
		while (tail->next)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

单链表的尾删 

注意:删除时链表不能为空因此需要断言,而插入时链表可以为空。

删除时有两步,将最后一个结点free掉并且将倒数第二个结点的next置为NULL。有两种办法,第一种是创建一个变量记录倒数第二个结点的地址,保障在free掉最后一个结点后仍能够找到倒数第二个结点并将它的next置为NULL;第二种办法如下:

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

单链表的头插

头插头删相对来说比尾插尾删要简单。但同样要注意使用的是二级指针。

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

单链表的头删

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

总的来说,重点掌握单链表的尾插尾删,头插头删就不成问题啦。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值