【数据结构】单链表的实现

1. 链表

之前我们实现了顺序表,接下来来学习链表,并且理解顺序表和链表的区别

1.1 链表的概念及结构

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

注意:

  1. 从上图可看出,链式结构在逻辑上是连续的,但是在物理上不一定连续
  2. 现实中的节点一般都是从堆上申请出来的
  3. 从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续

假设在32为系统上,节点中值域为int类型,则一个节点的大小为8个字节,则也可能有下述链表:
在这里插入图片描述

1.2 链表的分类

实际中链表的结构非常多样,一下情况组合起来就有8种链表结构:

  1. 单向或者双向
    在这里插入图片描述
  1. 带头或者不带头
    在这里插入图片描述
  1. 循环或者非循环
    在这里插入图片描述

以上情况进行随机组合,共有8种。
虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:
在这里插入图片描述

  1. 无头单向非循环链表结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
  2. 带头双向循环链表结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。

我们来实现这两种链表,那么其它链表就十分简单了。

1.3 链表的实现

和写顺序表一样,我们来写接口函数,来想一想链表的一些结构。

//首先我们得想一想节点的结构有哪一些?
typedef int SLTDataType;//想修改数据的类型在这里修改就足够了

typedef struct SLTNode {//创建节点,节点有值,有下一个节点的地址
	SLTDataType val;
	struct SLTNode* next;//next有下个节点的地址
}SLTNode;//重命名,有助于节省代码
//想好了节点的结构,那我们得创建节点了,写个创建节点函数。
//将想要赋数据值传过来
SLTNode* BuySLTNode(SLTDataType x)//创建节点就要向内存申请空间,那么返回空间的地址就行了
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)//如果开辟失败进行报错
	{
		perror("malloc fail");//开辟失败则返回错误信息
		return NULL;
	}
	newnode->next = NULL;//及时将第一个节点的next置为空,不能为野指针
	newnode->val = x;//将数据置为x   
	return newnode;
}
//申请完节点时,想一个问题,单链表能不能打印?我们知道顺序表是空的不能打印,顺序表是空的代表申请空间失败,但是单链表是空的可以打印
void SLTNodePrint(SLTNode* phead)//这里不用断言指针是否为空,因为空链表可以进行打印
{
	if (phead == NULL)
		return;
	SLTNode* cur = phead;//最好创建一个新的指针来接收地址
	while (cur)//循环遍历,一个一个数据打印,将cur指向下个节点,为空代表节点遍历完了
	{
		printf("%d->", cur->val);
		cur = cur->next;
	}
	printf("NULL\n");
}
//我们来进行数据的插入,首先是尾部插入节点
void SLTNodePushBack(SLTNode** pphead, SLTDataType x)
//这里使用二级指针是因为我们要修改一级指针的值。
{
	assert(pphead);//断言判断一级指针的地址不能为空
	SLTNode* newnode = BuySLTNode(x);//申请一个节点,返回地址
	if (*pphead == NULL)//如果为空链表直接将新的地址作为新的节点
	{
		*pphead = newnode;
	}
	else//链表不为空
	{
		SLTNode* tail = *pphead;
		while (tail->next)
		{
			tail = tail->next;//一个循环遍历,找到最后一个节点
		}
		tail->next = newnode;//将新的节点接到最后一个节点上,自己成为最后一个节点
	}
}	
//进行头部插入节点
//一样的,修改值就要传地址过来
void SLTNodePushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);//断言
	SLTNode* cur = *pphead;
	SLTNode* newnode = BuySLTNode(x);//头插申请一个新的节点
	newnode->val = x;
	newnode->next = cur;//让新的节点next指向之前的节点
	(*pphead) = newnode;//将指针指向的地址改变
}
//进行尾删
void SLTNodePopBack(SLTNode** pphead)
{
	assert(pphead);//断言
	if (*pphead == NULL)//要判断链表为空的情况
		return;
	SLTNode* tail = *pphead;
	SLTNode* prev = tail;
	while (tail->next)//找到最后一个节点
	{
		prev = tail;
		tail = tail->next;
	}
	free(tail);//删除链表时,要记得及时释放空间
	prev->next = NULL;//将前一个链表的next置为空,否则就是野指针
}
//进行头删
void SLTNodePopFront(SLTNode** pphead)
{
	assert(pphead);//断言
	if (*pphead == NULL)//当为空链表时,直接返回
		return;
	SLTNode* cur = *pphead;
	*pphead = cur->next;//将*pphead指向下一个节点的地址
	cur->next = NULL;//及时将指针置为空
	free(cur);
	
}

//进行插入节点操作,我们在指定节点后插入一个新的节点
//为什么不在指定节点插入节点呢?因为单链表没有存储上个节点的地址,只存储了下个节点的地址,所以在指定节点插入节点会有点麻烦,我们就在指定节点后插入一个节点。
void SLTNodeInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);//判断传的是不是空
	SLTNode* newnode = BuySLTNode(x);//申请一个新节点
	SLTNode* next = pos->next;//要记住后一个节点的位置
	pos->next = newnode;//在当前节点的next指向新节点
	newnode->next = next;//新节点的next指向指定节点的下一个节点
}
//进行删除操作
void SLTNodeEraseAfter(SLTNode* pos)
{
	assert(pos && pos->next);//要判断空或者只有一个节点的情况
	SLTNode* del = pos->next;
	pos->next = del->next;//要将指定节点的next指向被删除节点的next,要不然释放空间之后就是野指针了
	del->next = NULL;
	free(del);//记得释放空间
}
//进行数据的查找,找到了返回节点的地址
SLTNode* SLTNodeSerach(SLTNode*phead, SLTDataType x)
{
	assert(phead);//如果为空则不用找
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->val == x)
			return cur;//找到了就返回当前节点的地址
		cur = cur->next;//指向下一个节点
	}
	return NULL;//没找到就返回空
}
//进行链表的销毁
void SLTNodeDestroy(SLTNode** pphead)
{
	assert(pphead);//断言
	if (*pphead == NULL)//空链表就不用销毁了
		return;
	SLTNode* cur = *pphead;
	SLTNode* del = *pphead;
	while (cur)
	{
		del = cur;//一个要删除节点的地址
		cur = cur->next;//一个保存下个节点的地址
		del->next = NULL;
		free(del);//释放空间
	}
}

1. 4 结尾

当前我们学习单链表,下次学习双向链表,自己也可以去力扣网站上刷一些题,来巩固链表相关的知识,我也会不定时写一些我自己的解题看法
https://leetcode.cn/tag/linked-list/problemset/

o_0

龙吟虎啸,凤翥鸾翔,大丈夫之气象。—格言联璧

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值