单链表理解使用

本文介绍了链表的基本概念,通过结构体定义链表节点,包括数据和指向下一个节点的指针。详细阐述了如何创建节点、连接节点、打印链表、尾插、尾删、头插、头删、查找节点、修改节点以及销毁链表的实现方法,涉及内存管理和指针操作。
摘要由CSDN通过智能技术生成

概念

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

就像是古代的驿站,找到一个便能顺着找到下一个。所以我们就可以粗浅的明白,什么是链表了。

即逻辑上连续的一串指针表格,但是在物理上不一定连续。这种就像是我们画出来的思维导图,在思维上方便我们理解。至于为什么在物理上不一定连续,我们在后面会一一讲到,请读者稍安勿躁。

实现

既然我们已经能粗浅的理解了概念,那么就可以简单的上手操作了。

链表结构体的构建

在构建它的时候,我们知道,它里面既有一个数据,又有指向下一个数据地址的指针;同时这个指针也要是结构体的指针,我们需要同时满足这两个条件,所以我们应用结构体来满足这个要求:

至于将int重命名主要是因为后面使用会很多,所以我们进行重命名,方便后面进行调用。

//结构体构建
typedef int SLTDataType;
typedef struct SListNode
{
	SLTDataType data;//数据
	struct SListNode* next;//指针
}SLTNode;

节点的构建

我们已经将它需要的结构体搭建完成,那么我们应该将链表的一个一个小的节点整理出来,将构成函数的最基本的点搭建出来:

SLTNode* BuySLTNode(SLTDataType x)//购置单个节点
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));//用malloc创建新的节点
	if (newnode == NULL)//小心开辟失败
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->data = x;//将数据放置其中
	newnode->next = NULL;//指针指向定为空

	return newnode;//返回这个节点的地址
}

节点的链接

将一个个节点搭建出来之后,我们就可以用绳子(指针)将他们串联起来:

SLTNode* CreatLink(int n)
{
	SLTNode* phead = NULL;
	SLTNode* ptail = NULL;
	//int x = 0;
	for (int i = 0; i < n; i++)
	{
		SLTNode* newnode = BuySLTNode(i);
		if (phead == NULL)
		{
			phead = ptail = newnode;//如果头部一开始为空,赋值
		}
		else
		{
			ptail->next = newnode;//如果有头部,那么将后面的节点从后面连接到头部结点之后形成尾
			ptail = newnode;
		}
	}
	return phead;//返回这一串链表的头节点
}

如何查看链表

我们能够将链表连接起来了,那么我想展示一下,如何展示让我们自己看到呢?
简单,构建一个函数就好了啊(doge)。

void Print(SLTNode* phead)
{
	SLTNode* cur = phead;//把头部指针赋值下来
						 //不用头部指针是为了防止找不到头部,这是一个好的习惯哦
	while (cur != NULL)
	{
		printf("[ %d | %p ]->", cur->data, cur->next);//数据以及指针。一一对照
		cur = cur->next;
	}
	printf("NULL\n");
}

好,到这里,我们的链表就结束了…
当然不可能了。这么简单的事情,怎么能体现出我们的厉害程度呢?所以下面 正菜开始:

链表的尾插

我们需要
1.找到尾部
2.将一个新的节点插入到里面

void SLTPushBack(SLTNode** pphead,SLTDataType x)//为什么会用二级指针呢?
{

	SLTNode* newnode = BuySLTNode(x);//新节点
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//找尾
		SLTNode* perv = *pphead;
		while (perv->next)
		{
			perv = perv->next;
		}
		perv->next = newnode;//尾部插入
	}
}

使用二级指针是因为我i们将头部指针的位置进行改变,由于形参不能影响的实参,只是一份临时拷贝,所以我们应该使用二级指针来进行传址调用

尾删

1.找到尾部
2.删除尾部节点,将前一个的next置空,并且释放掉旧的节点
3.小心过多的删除导致越界

void SLTPopBack(SLTNode** 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);//为什么不将tail置空呢?
		prev->next = NULL;
	}
}

形参只是实参的一份临时拷贝,所以,将tail置空没有意义。

头插

1.新的节点 链接 到旧的头节点前
2.返回新的头节点的指针

void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuySLTNode(x);
	newnode->next = *pphead;//插到头的前面
	*pphead = newnode;//新头部的地址
}

头删

1.找到头
2.删除头,返回新的头节点地址
3.小心删除空的链接导致越界

void SLTPopFront(SLTNode** pphead)
{
	assert(pphead);
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}

查找单个的节点

1.对比节点中的数值是否相等
2.返回找到的值的地址

SLTNode* SLTFind(SLTNode* phead,SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;//没找到返回空
}

到这里呢,单链表基本的增删查已经完成了。
下面就是超·进阶奥义:在查找到的节点进行修改

在查找到的地方进行节点的修改

//在pos后插入x
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	SLTNode* newnode = BuySLTNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}
//在pos前插入x
void SLTInsertBefore(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pos);
	if (pos == (*pphead))
	{
		SLTPushFront(pphead,x);//如果头为空,相当于头插
	}
	else
	{
		SLTNode* newnode = BuySLTNode(x);
		SLTNode* cur = *pphead;
		while (cur->next != pos)//找pos位的地址
		{
			cur = cur->next;
		}
		newnode->next = cur->next;
		cur->next = newnode;
	}
}
//把pos后面清除
void SLTEraseAfter(SLTNode* pos)
{
	if (pos->next == NULL)
	{
		return;
	}
	else
	{
		SLTNode* nextNode = pos->next;
		pos->next = nextNode->next;
		free(nextNode);
	}
}
//删除当前位置
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pos);
	assert(*pphead);

	if (*pphead == pos)
	{
		SLTPopFront(pphead);//若在头部,相当于头删
	}
	else//其余位置相对简单
	{
		SLTNode* cur = *pphead;
		while (cur->next != pos)
		{
			cur = cur->next;
		}
		cur->next = pos->next;

		free(pos);
	}
}

销毁

我们知道,单链表使用malloc开辟出来的节点,即存在于堆区上,不主动释放的话,如果在大型的程序上可能会导致崩溃掉
所以

//释放单链表
void SLTDestroy(SLTNode** pphead)
{
	SLTNode* cur = *pphead;
	while (cur)
	{
		SLTNode* nextNode = cur->next;//从头到尾一次释放
		free(cur);
		cur = nextNode;
	}
	*pphead = NULL;
	//是否制空,这里面都是形参,所以置不置空都无所谓。
	//但是使用SLTDestroy时的传输进入的一级指针需要销毁。即*pphead
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

青青丘比特

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

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

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

打赏作者

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

抵扣说明:

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

余额充值