讲解如何用C语言实现带头单向单链表

目录

链表的结构组成:

链表的常用接口:

初始化带头单链表的头指针:

动态申请一个结点(用于后续数据的插入):

单链表打印:

单链表尾插:

单链表的头插:

单链表的尾删:

单链表头删:

单链表查找符合值的第一个节点,没找到时返回NULL:

单链表在pos位置之后插入x:

单链表删除pos位置之后的值:

致谢:


链表的结构组成:

在学习链表之前,我们先来简单认识一下链表这个结构(本篇文章均用链表存储整型数据进行讲解)(本篇文章针对的是带头单向单链表的实现)

#pragma once
#include <stdio.h>
typedef int SLTDateType;
typedef struct SListNode
{
	SLTDateType data;
	struct SListNode* next;
}SListNode;

链表整体结构是由一个头结点,和多个结点连接到一起构成的一种数据结构,尾结点的next指针指向空。每个结点由携带数据和next指针构成。是一种便于头部插入删除的一种数据结构。

链表的常用接口:

下面是我们要实现的链表的一些常用接口:

//初始化带头单链表的头指针
void SListNodeInit(SListNode** pplist);
// 动态申请一个结点(用于后续数据的插入)
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode* plist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode* plist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode* plist);
// 单链表头删
void SListPopFront(SListNode* plist);
// 单链表查找符合值的第一个节点
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos);

初始化带头单链表的头指针:

带头单链表结构的头指针指向的是一个不存放数据的无效结点,只是为了方便头部的插入删除操作。

//初始化带头单链表的头指针
void SListNodeInit(SListNode** pplist)
{
	(*pplist) = (SListNode*)malloc(sizeof(SListNode));
	(*pplist)->next = NULL;
}

从上面的代码我们可以看出来一个问题,那就是我为什么在这个函数里传的参数是二级指针呢?

答:因为上面函数的目的是要改变单链表的头指针的指向,我要去改变这个头指针的话只能传这个头指针的地址,运用传址调用函数这个方法改变头指针。

动态申请一个结点(用于后续数据的插入):

// 动态申请一个结点(用于后续数据的插入)
SListNode* BuySListNode(SLTDateType x)
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

单链表打印:

单链表的打印我们只需要从第一个有效节点去遍历这个链表就可以了。

我们这里只需要从这里的newphead这个节点开始遍历就可以了。

// 单链表打印
void SListPrint(SListNode* plist)
{
	SListNode* newphead = plist->next;
	SListNode* ptail = newphead;
	while (ptail!=NULL)
	{
		printf("%d->", ptail->data);
		ptail = ptail->next;
	}
	printf("NULL\n");
}

结束条件就是ptail指针指向NULL的时候。

单链表尾插:

// 单链表尾插
void SListPushBack(SListNode* plist, SLTDateType x)
{
	SListNode* newnode = BuySListNode(x);
	SListNode* ptail = plist;
	while (ptail->next != NULL)
	{
		ptail = ptail->next;
	}
	ptail->next = newnode;
}

单链表的头插:

头插由于有一个头结点,所以头插比较容易一些。

头插一定注意先保留plist->next给newnode->next,因为如果先对头结点的next指针进行操作的话会导致后面的数据丢失。

// 单链表的头插
void SListPushFront(SListNode* plist, SLTDateType x)
{
	SListNode* newnode = BuySListNode(x);
	newnode->next = plist->next;
	plist->next = newnode;
}

单链表的尾删:

// 单链表的尾删
void SListPopBack(SListNode* plist)
{
	assert(plist->next);
	SListNode* ptail = plist;
	while (ptail->next->next)
	{
		ptail = ptail->next;
	}
	free(ptail->next);
	ptail->next = NULL;
}

单链表头删:

思路:先拿ptail指针记录下第二个有效节点的位置,然后删除第一个数据,最后将plist节点和ptail节点连接上。

// 单链表头删
void SListPopFront(SListNode* plist)
{
	assert(plist->next);
	SListNode* ptail = plist;
	ptail = ptail->next->next;
	free(plist->next);
	plist->next = ptail;
}

单链表查找符合值的第一个节点,没找到时返回NULL:

这个接口也是遍历链表。

// 单链表查找符合值的第一个节点,没找到返回NULL
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
	SListNode* ptail = plist->next;
	while (ptail)
	{
		if (ptail->data == x)
		{
			return ptail;
		}
		ptail = ptail->next;
	}
	printf("抱歉,没有找到\n");
	return NULL;
}

单链表在pos位置之后插入x:

这个插入方法和我上面讲解的头插方法类似,因此直接代码奉上,不做过多讲解。

// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
	SListNode* newnode = BuySListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

单链表删除pos位置之后的值:

这个删除方法和我上面讲解的头删方法类似,因此直接代码奉上,不做过多讲解。

// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos)
{
	assert(pos->next);
	SListNode* ptail = pos;
	ptail = ptail->next->next;
	free(pos->next);
	pos->next = ptail;
}

致谢:

至此,我们的带头单向单链表的常用接口就已经实现完毕了,后续,本管家还会持续更新数据结构方面的内容,同时也会穿插着数据结构常见的算法题进行讲解。

同时也希望读者能够在评论区给我的文章提出一些建议,也可以在我讲解内容方面哪里做的比较好的地方在评论区点出来,我会继续发扬光大,不好的地方我也会接受批评;读者的一些点赞、收藏和评论也是对本管家的一些支持与鼓励,如果您看完这篇有所收获,还请您点个免费的赞和收藏,我会更有动力!感谢观看!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值