单链表c语言实现

1.单链表

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。

链表的结构是多种多样的,可以分为单向或双向,带头结点或不带头结点,循环或者非循环的。今天我们来实现一个单向不带头结点不循环的单链表。

image-20221114202720517

2.代码实现

SingleList.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SListDataType;
typedef struct SingleListNode
{
	SListDataType val;
	struct SingleListNode* next;
}SListNode;

SListNode* BuySListNode(SListDataType data);//创建节点
void SListPushBack(SListNode** pplist, SListDataType data);
void SListPopBack(SListNode** pplist);
void SListPushFront(SListNode** pplist, SListDataType data);
void SListPopFront(SListNode** pplist);
void SListPrint(SListNode* plist);
SListNode* SListFind(SListNode* plist, SListDataType data);
void SListAfterInsert(SListNode* pos, SListDataType data);//在pos位置之后插入
void SListBeforeInsert(SListNode** pplist, SListNode* pos, SListDataType data);//在pos位置之前插入
void SListErase(SListNode** pplist, SListNode** ppos);//删除pos位置的元素
void SListAfterErase(SListNode* plist, SListNode* pos);//删除pos位置下一个的元素

SingleList.c

#include "SingleList.h"

SListNode* BuySListNode(SListDataType data)
{
	SListNode* newNode = (SListNode*)malloc(sizeof(SListNode));
	if (newNode == NULL)
	{
		printf("malloc fail\n");
		exit(1);
	}
	newNode->val = data;
	newNode->next = NULL;
	return newNode;
}

void SListPushBack(SListNode** pplist, SListDataType data)
{
	assert(pplist);
	if (*pplist == NULL)
	{
		//第一次插入
		*pplist = BuySListNode(data);
		return;
	}
	SListNode* cur = *pplist;
	while (cur->next)
	{
		cur = cur->next;
	}
	cur->next = BuySListNode(data);
}

void SListPopBack(SListNode** pplist)
{
	assert(pplist);
	SListNode* prev = NULL;
	SListNode* cur = *pplist;
	if (cur == NULL)
	{
		printf("链表中无节点\n");
		return;
	}
	else if (cur->next == NULL)
	{
		//只有一个节点
		free(cur);
		cur = NULL;
		*pplist = NULL;
	}
	else
	{
		while (cur->next)
		{
			prev = cur;
			cur = cur->next;
		}
		free(cur);
		cur = NULL;
		prev->next = NULL;
	}
}

void SListPushFront(SListNode** pplist, SListDataType data)
{
	assert(pplist);
	SListNode* cur = *pplist;
	if (NULL == cur)
	{
		//第一次插入,调用尾插即可
		SListPushBack(pplist, data);
		return;
	}
	else
	{
		SListNode* prev = BuySListNode(data);
		prev->next = cur;
		*pplist = prev;
	}
}

void SListPopFront(SListNode** pplist)
{
	assert(pplist);
	SListNode* cur = *pplist;
	if (cur == NULL)
	{
		printf("链表中无节点\n");
		return;
	}
	else if (cur->next == NULL)
	{
		//链表中只有一个节点,调用尾删即可
		SListPopBack(pplist);
		return;
	}
	else
	{
		SListNode* next = cur->next;
		free(cur);
		*pplist = next;
	}

}

SListNode* SListFind(SListNode* plist, SListDataType data)
{
	SListNode* cur = plist;
	while (cur)
	{
		if (cur->val == data)
		{
			printf("找到了\n");
			return cur;
		}
		cur = cur->next;
	}
	printf("没找到\n");
	return NULL;
}

void SListAfterInsert(SListNode* pos, SListDataType data)
{
	assert(pos);
	SListNode* cur = pos;
	SListNode* next = cur->next;
	SListNode* newNode = BuySListNode(data);
	cur->next = newNode;
	newNode->next = next;
}
void SListBeforeInsert(SListNode** pplist, SListNode* pos, SListDataType data)
{
	//首先,这里不建议在pos前面的位置插入,因为是单链表,pos的前一个位置我们是不清楚的,除非再遍历一遍。
	//此外,比如说链表中只有一个元素,在这个元素前面插入,还涉及到改变头结点的指向,这就需要传入二级指针
	assert(pplist && pos);
	SListNode* cur = *pplist;
	SListNode* prev = NULL;
	if (cur == pos)
	{
		//在第一个元素前面插入,此时相当于头插,直接调用头插
		SListPushFront(pplist, data);
		return;
	}
	while (cur != pos)
	{
		prev = cur;
		cur = cur->next;
	}
	SListNode* newNode = BuySListNode(data);
	prev->next = newNode;
	newNode->next = cur;
}

void SListAfterErase(SListNode* plist, SListNode* pos)
{
	assert(plist && pos);
	if (pos->next == NULL)
	{
		//pos就是最后一个元素
		printf("pos后面没有元素可删除\n");
		return;
	}
	SListNode* next = pos->next->next;
	free(pos->next);
	pos->next = next;
	
}
void SListErase(SListNode** pplist, SListNode** ppos)
{
	//首先这里不推荐删除pos位置的元素,比较麻烦,因为单链表我们不清楚pos位置前面的元素。建议删除pos位置后面的那个元素
	//注意,这里一定要传两个二级指针
	//1.第一个参数:因为如果pos位置是第一个节点的位置,那么涉及到头结点指向的改变
	//2.第二个参数:删除pos位置的节点要free(pos),此后pos应该置为空指针。由于形参只是实参的一份临时拷贝
	//如果传一级指针,我们只是将这个临时拷贝置为了空指针,而真正的pos仍然指向已经被free了的空间,这显然不合适
	//另外,我们再函数中创建了变量cur也指向了pos所指向的空间,那么释放掉这部分空间之后需不需要将cur置为NULL呢?
	//并不需要,因为cur是在函数体内创建的一个变量,在函数调用结束后,随函数栈帧销毁而销毁了
	assert(pplist && ppos);
	SListNode* cur = *pplist;
	SListNode* prev = NULL;
	if (cur == *ppos)
	{
		//相当于头删,直接掉头删就行
		SListPopFront(pplist);
		*ppos = NULL;//这里不要忘了把pos置空,头删可不会帮你把pos置空
		return;
	}
	while (cur != *ppos)
	{
		prev = cur;
		cur = cur->next;
	}
	//在删除当前节点之前,我们先把当前节点的下一个节点记录下来
	SListNode* next = cur->next;
	free(cur);
	*ppos = NULL;
	prev->next = next;
}
void SListPrint(SListNode* plist)
{
	while (plist)
	{
		printf("%d ", plist->val);
		plist = plist->next;
	}
	printf("\n");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逃跑的机械工

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值