如何使用C语言实现单向链表?

一、单向链表的结构

链表的物理结构
image-20231221222113929.png

二、C语言实现链表

写代码的IDE是vs2019

1、使用结构体定义一个节点的原型,包括存储数据的data和指向下一个节点的指针next。

//链表的结构体原型
typedef int SLDatatype;
typedef struct SListNode
{
	SLDatatype data;//数据
	struct SListNode* next;//下一个节点的地址

}SLTNode;

2、利用next遍历整个链表,直到遇到NULL,打印链表数据

//链表打印
void SLTPrint(SLTNode* phead)
{
    //空链表也可以打印,也没有必须要断言
	SLTNode* cur = phead;//链表的头指针

	while (cur)//当地址不为空
	{
		printf("%d->", cur->data);//打印节点里面的数据
		cur = cur->next;//把下一个节点的地址赋值到当前地址
	}
	printf("NULL\n");

}

3、开辟新节点,插入链表的头

//链表头插 
void SLPushFront(SLTNode**  phead, SLDatatype x)
{
	//assert(phead);

	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));//创建一个新节点
	if (newnode == NULL)
	{
		perror("malloc fail:");
		return;
	}


	newnode->data = x;//新节点赋新值
	newnode->next = NULL;//新节点初始化next地址

	newnode->next = *phead;//新节点的next赋值上一个链表的指针
	*phead = newnode;//新节点的指针赋值成为链表的指针
}

```c
//创建节点并赋值和置空 
SLTNode* BuyLTNode(SLDatatype x )
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail:");
		return;
	}

	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}
//链表头插 简化版
void SLPushFront(SLTNode**  pphead, SLDatatype x)
{
	assert(pphead);//这里需要断言,因为如果这里是空的话,代表没有链表,它是头指针plist的地址
	//assert(*pphead)//这里不需要断言,因为链表就算是空的也可以头插
	SLTNode* newnode = BuyLTNode(x);
    
	newnode->next = *pphead;
	*pphead = newnode;
}

4、开辟新节点

//链表尾插
void SLPushBack(SLTNode** pphead, SLDatatype x)
{
	assert(pphead);//plist头指针不能为空,否则就是链表都不存在
	//assert(*pphead);链表为空,可以尾插,不需要断言

	//1、空链表
	//2、非空链表
	if (*pphead == NULL)
	{
		*pphead = BuyLTNode(x);
	}
	else
	{
		SLTNode* tail = *pphead;
		//SLTNode* tmp = NULL;
		/*while (tail)
		{  
			tmp = tail;
			tail = tail->next;

		}
		tmp->next = BuyLTNode(x);*/
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = BuyLTNode(x);

	}
}

5、释放头结点,并且连起下一个节点

//头结点删除
void SLPopFront(SLTNode** pphead)
{
	//空链表
	assert(pphead);//这里需要断言,因为这里为空就是代表头指针plist的地址都有
	assert(*pphead);//这里需要断言,因为这里为空代表链表一个节点都没有,你怎么头删呢
	//非空 
	SLTNode* tmp = *pphead; 
	*pphead = (*pphead)->next;
	free(tmp);
}

6、释放尾节点,并且next置空

//尾节点删除
void SLPopBack(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead);//链表为空 
	//只有一个节点
	if ((*pphead)->next==NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	// 多个节点
	else
	{
		SLTNode* tail = *pphead;
		SLTNode* prev = NULL;
		while (tail->next)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		prev->next = NULL;

		//写法二
		/*while (tail->next->next)
		{
			tail = tail->next;
		}

		free(tail->next);
		tail->next = NULL;*/
	}
	
}

7、遍历查找,返回找到的地址

//单链表查找
SLTNode* STFind(SLTNode* phead, SLDatatype x)
{
	//assert(phead);//没必要断言,空链表查找了一下,查不到也没有问题, 就是找不到啊
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;

}

8、中间插入节点

//在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLDatatype x)
{
	assert(pphead);//链表的头指针要断言
	assert(pos);//pos是你要插入的数字的节点的地址,如果是空就是尾插了,这样也可以不断言
	//assert(*pphead)//*pphead断言就是空链表,空链表插入那就是pos为NULL,所以没有必要断言*pphead

	if (*pphead == pos)
	{
		SLPopFront(pphead, x);
	}
	else
	{
		SLTNode* newnode = BuyLTNode(x);
		SLTNode* cur = *pphead;
		SLTNode* end = NULL;
		while (cur)
		{

			if (cur == pos)
			{
				newnode->next = pos;
				end->next = newnode;
				break;
			}
			end = cur;
			cur = cur->next;
		}
	}
	
}
//在pos之后插入
void SLInsertAfter(SLTNode** pphead, SLTNode* pos, SLDatatype x)
{
	assert(pos);

	SLTNode* newnode = BuyLTNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
	

}

9、中间删除节点

//删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(*pphead);

	if (*pphead == pos)
	{
		SLPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
	}
 
}
//删除pos后一位节点
void SLEraseAfter(SLTNode* pos)
{
	assert(pos);//不能删除空指针的后一位
	assert(pos->next);//最后一个节点的后一位就没有了,所以要断言

	SLTNode* next = pos->next;
	pos->next = next->next;
	free(next);

	/*pos->next = (pos->next)->next;
	free(pos->next);*/

}

10、释放链表

//链表的释放
void SLDestory(SLTNode** pphead)
{
	assert(pphead);
	SLTNode* cur = *pphead;
	/*while (cur)
	{
		SLTNode* del = cur;
		cur = cur->next;
		free(del);
	}*/
	while (cur)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}

	*pphead = NULL;//plist置空,如果函数用一级指针这里不能置空,需要手动在外部把plist置空

}

  • 19
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Hhh __灏

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

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

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

打赏作者

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

抵扣说明:

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

余额充值