C语言实现单链表的增删查改

一、什么是链表

链表是一种再=在物理上存储是非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接依次实现的。
链表有双链表和单链表。
单链表如图所示:
在这里插入图片描述
上一个结点保存自己的数据和下一个数据的地址,这就是单链表,通过这样的形式把所有结点链接起来。
双链表如图所示:
在这里插入图片描述双链表可以通过指针访问后一个元素,也可以访问前一个元素。

二、多文件实现单链表

1、自己编写的头文件SList.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#pragma warning(disable:4996)


typedef int SLTDateType;
typedef struct SListNode//把结点保存在结构体中
{
	SLTDateType date;
	struct SListNode* next;

}SListNode;

//函数声明
//打印链表函数
void PrintSListNode(SListNode* phead);

//链表尾插函数
void SLNodePushBack(SListNode** pphead, SLTDateType x);

//链表尾删函数
void SLNodePopBack(SListNode** pphead);

//链表头插函数
void SListPushFront(SListNode** pphead, SLTDateType x);

//链表头删函数
void SListNodePopFront(SListNode** pphead);

//链表中在x之后插入函数
void SListNodeInsertAfter(SListNode*pos, SLTDateType x);

//链表查找函数
SListNode* FindSListNode(SListNode* phead, SLTDateType pos);

//链表删除pos位置之后(中间删除)的函数
void SListNodeEraseaAfter(SListNode* pos);
```c
3、源文件SList.c
在SList.c中主要实现单链表的增删查改接口(函数),一定要包含自己写的头文件。
(1.动态申请结点函数:
在我们需要增加数据的时候才会申请结点,并将插入的值给新结点的date,
返回新结点的地址给其他结点(他的前一个或者后一个)保存
```c
SListNode* BuySListNode(SLTDateType x)//创建一个新的结点
{
	SListNode* newNode = (SListNode*)malloc(sizeof(struct SListNode));//用malloc函数申请一个节点的大小,
	if (newNode == NULL)
	{
		printf("malloc error\n");
		exit(-1);
	}
	newNode->date = x;//将插入的值给新结点的date
	newNode->next = NULL; //新结点的next为NULL

	return newNode;//返回新结点的地址
}

(2).打印链表函数:
从链表的头到尾依次打印,cur==NULL时为链表的尾。

void PrintSListNode(SListNode* phead)
{
	struct SListNode* cur = phead;
	while (cur!= NULL)//从头开始打印,直到最后一个的next为空
	{
		printf("%d->", cur->date);
		cur = cur->next;
	}
	printf("NULL");
}

(3)、链表尾插函数
有两种情况: 1 链表中 没有结点,没有结点,
此时 pphead==NULL;将增加的值给新结点,将新结点的地址给pphead如图:
在这里插入图片描述2、有多个结点
插入时,首先要找到链表的尾,然后将尾(tail),tail->next=新结点的地址,
还是画图比较容易理解:
在这里插入图片描述代码如下:

void SLNodePushBack(SListNode** pphead, SLTDateType x)
{
	//尾插有两种情况 1 没有结点
	//               2 有结点
	struct SListNode* newNode = BuySListNode(x);//插入就需要创建新的结点
	if (*pphead == NULL)
	{
		*pphead = newNode;//将新的结点地址给头(也是尾)
	}
	else
	{
		struct SListNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		
		tail->next = newNode;//把新的结点地址给尾
	}

}

(3)链表的尾删函数:
尾删分三种情况:1 、没有结点
直接退出,不用删除
2 、只有一个结点
释放掉,然后让链表的头为空(*pphead=NULL)

                          3  、2个及以上
					找到最后一个和他的前一个,然后释放掉最后一个,最后一个的前一个(perv),让perv->next为空
					画图理解一下:

在这里插入图片描述代码如下:

void SLNodePopBack(SListNode** pphead)
{
	//尾删分三种情况:1 没有结点
	//            2 只有一个结点
	//			  3 2个及以上
	
	if (*pphead == NULL)
	{
		return;//没有结点直接退出函数
	}
	else if ((*pphead)->next == NULL)
	{
		free(*pphead);//有一个结点,释放掉,并让其指向空
		*pphead = NULL;
	}
	else
	{
		struct SListNode* tail = *pphead;
		struct SListNode* perv = NULL;
	//有多个结点,先找到最后一个,然后释放掉,再让倒数第二个结点next为空
		while (tail->next!= NULL)
		{
			perv = tail;
			tail = tail->next;
		}
		free(tail);
		perv->next = NULL;
	}
}

(4)、链表的头插函数
头插就是将要添加的数据当作链表新的头,只要将原来头的地址给新结点的next,再将newNode的地址给*pphead作为新的头。
如图:
在这里插入图片描述

代码如下:

`void SListPushFront(struct SListNode** pphead, SLTDateType x)
{
	struct SListNode* newNode = BuySListNode(x);
	newNode->next = *pphead;
	*pphead = newNode;//newNode是新的头
}`

(5)头删函数
将链表的第一个结点删除
分三种情况1 没有结点
如果链表为空,直接退出
// 2 、只有一个结点
// 3 、2个及以上
2和3可以兼容
有两种方法,第一种直接让第二个结点的地址作为头
第二种:先保存下一个结点的地址,再将第一个二结点释放,然后将第二个地址给到*pphead
画图理解一下:
在这里插入图片描述

void SListNodePopFront(SListNode** pphead)
{    
 //分三种情况1 没有结点
		//   2 只有一个结点
		//	 3 2个及以上  2.3可以兼容
	if (*pphead == NULL)
	{
		return;
	}
	else
	{
		//两种方法,第一种直接让第二个结点的地址作为头
		//*pphead = (*pphead)->next;

		//第二种如下:
		SListNode* cur = (*pphead)->next;//保存第二个地址
		free(*pphead);//释放第一个
		*pphead = NULL;
		*pphead= cur;//让第二个成为新的头
	}
}					

(6)、查找函数
将你要查找的值,与链表中所有的结点的date对比,如果有就返回他的地址
代码如下:

SListNode* FindSListNode(SListNode* phead, SLTDateType pos)
{
	assert(phead);
	SListNode* cur = phead;
	while (cur!= NULL)
	{
		if (cur->date != pos)
		{
			cur = cur->next;
		}
		else
		{
			break;
		}
	}
	if (cur!=NULL)
	{
		printf("找到了\n");
		return cur;
	}
	else
	{
		printf("不存在\n");
		return NULL;
	}
}
(7)、中间插入函数
给定一个位置(pos),再他之后添加
   1、先将pos下下一个的地址赋值给新结点(newNde)的next,再将新结点的地址赋值给pos的next,这样就链接起来了
   如图所示:

在这里插入图片描述代码如下:

void SListNodeInsertAfter(SListNode* pos, SLTDateType x)
{
	assert(pos);
	//SListNode* newNode = BuySListNode(x);
	//SListNode* temp = pos->next;//先保存pos下一个结点的地址
	//pos->next = newNode;//将新结点地址给到pnextNode->next
	//newNode->next =pos->next->next;	
	SListNode*newNode= BuySListNode(x);
	newNode->next = pos->next;
	pos->next = newNode;
}

(8)、中间删除函数

给定一个位置(pos),紧跟之后的一个删除
将pos的下下一个节点的地址赋给pos->next
代码如下:

void SListNodeEraseaAfter(SListNode* pos)
{
	SListNode* next = pos->next;//pos下一个地址
	SListNode* nextnext = next->next;//pos下下一个结点地址
	pos->next = nextnext;
	free(next);//释放pos之后的第一个(next)
	next = NULL;
}

三、代码运行结果

测试代码在源文件main.c 中
代码如下:

#include"SList.h"
void text()
{
	SListNode* ps = NULL;
	SLNodePushBack(&ps, 1);
	SLNodePushBack(&ps, 2);
	SLNodePushBack(&ps, 3);
	SLNodePushBack(&ps, 4);
	SLNodePushBack(&ps, 5);
	SLNodePushBack(&ps, 6);
	SLNodePushBack(&ps, 7);
	SLNodePushBack(&ps, 8);

	SListPushFront(&ps, 10);
	PrintSListNode(ps);
	printf("\n");
	SListNodePopFront(&ps);
	PrintSListNode(ps);
	printf("\n");
	SListNode* pos = FindSListNode(ps, 5);
	SListNodeInsertAfter(pos, 22);
	PrintSListNode(ps);
	printf("\n");
	SListNode* pos2 = FindSListNode(ps, 2);
	SListNodeEraseaAfter(pos2);
	PrintSListNode(ps);
	printf("\n");


}
int main()
{
	text();//测试函数
	return 0;
}

下边测试代码不一定与main.c中的代码相同
1、尾插,尾删测试如下图:
在这里插入图片描述
2、头删,头插函数如下图:
在这里插入图片描述3、链表查找函数测试结果如下:
在链表中找5:
在这里插入图片描述
在链表中找7:
在这里插入图片描述

4、中间插入,中间删除
在这里插入图片描述在5之后添加了22,删除了2之后的3。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值