实现了单链表各种功能,并配上详细解读。

链表的概念及结构

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

链表的分类

单向或双向
在这里插入图片描述
在这里插入图片描述
带头或不带头
在这里插入图片描述
在这里插入图片描述
循环或不循环
在这里插入图片描述
在这里插入图片描述
虽然有这么多的链表结构,但是我们常用的就两种
无头单向不循环链表
在这里插入图片描述
带头双向循环链表
在这里插入图片描述

链表的实现

实现的是一个无头的单链表

初始化

	//链表的初始化
	SLTNode* plist = NULL;

打印

//打印
void SListPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULLn");
}

申请结点

//申请结点
SLTNode* BuySlistNode(SLTDatatype x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

头插

//头插
void SListPushFront(SLTNode* phead, SLTDatatype x)
{
	SLTNode* newnode = BuySListNode(x);
	newnode->next = phead;
	phead = newnode;
}

根据前面的代码,我们头插结点,并打印试试看看代码对不对?
在这里插入图片描述
打印NULL,难道是我们代码写错了吗?
考虑这样一个问题,头插的时候,我们需要改变头指针的地址,那就调试一下看看,地址到底改变了吗
在这里插入图片描述
在这里插入图片描述
看到这里发现了问题,形参的改变并不会影响实参
举一个相似但很简单的例子,比如交换x,y的值
在这里插入图片描述
这里也是形参的改变并不会影响实参,那我们改如何改变x,y的值呢?答案是传址调用
在这里插入图片描述
改变int类型我们传地址,如果改变int* 我们该传什么呢?答案是传int* 的地址
在这里插入图片描述
在这里插入图片描述
所以我们得出这样的结论,改变结构体就传结构体的地址改变结构体指针就传结构体指针的到地址。
修改一下头插的代码。

//头插
void SListPushFront(SLTNode** pphead, SLTDatatype x)
{
	assert(pphead);
	SLTNode* newnode = BuySListNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

在这里插入图片描述

尾插

//尾插
void SListPushBack(SLTNode** pphead, SLTDatatype x)
{
	assert(pphead);
	SLTNode* newnode = BuySListNode(x);
	//链表为空
	//链表不为空
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

看到这里有些人或许又有点不解,为什么这里cur不用二级指针?
在这里插入图片描述
其原因是因为cur访问的结构体里面的内容,并没有要改变指针。
这和上面的一定要做区分

头删

//头删
void SListPopFront(SLTNode** pphead)
{
	assert(pphead);
	//检查链表是否为空
	assert(*pphead);

	SLTNode* del = *pphead;
	*pphead = (*pphead)->next;
	free(del);
	del = NULL;

}

尾删

//尾删
void SListPopBack(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	//方法1
	//if ((*pphead)->next == NULL)
	//{
	//	free(*pphead);
	//	*pphead = NULL;
	//}
	//else
	//{
	//	SLTNode* del = *pphead;
	//	SLTNode* prev = NULL;

	//	while (del->next != NULL)
	//	{
	//		prev = del;
	//		del = del->next;
	//	}
	//	free(del);
	//	del = NULL;
	//	prev->next = NULL;
	//}

	//法2
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLTNode* del = *pphead;
		while (del->next->next != NULL)
		{
			del = del->next;
		}
		free(del->next);
		del->next = NULL;

	}

}

查找

//查找
SLTNode* SListFind(SLTNode* phead, SLTDatatype x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
			return cur;
		cur = cur->next;
	}
	return NULL;
}

在pos位置之后插入

//在pos位置之后插入
void SListInsertAfter(SLTNode* pos, SLTDatatype x)
{

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

}

在pos位置之前插入

//在pos位置之前插入
void SListInsertBefor(SLTNode** pphead, SLTNode* pos, SLTDatatype x)
{
	assert(pphead);
	assert(pos);
	if (pos == *pphead)
	{
		SListPushFront(pphead, x);
	}
	else
	{
		SLTNode* newnode = BuySListNode(x);
		//寻找pos之前位置
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		newnode->next = pos;
		prev->next = newnode;
	}

}

删除pos位置之后的值

//删除pos位置之后的值
void SListEraseAfter(SLTNode* pos)
{
	assert(pos);
	if (pos->next == NULL)
	{
		return;
	}
	else
	{
		SLTNode* del = pos->next;
		pos->next = del->next;
		free(del);
	}

}

删除pos位置的值

//删除pos位置的值
void SListErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);
	if (pos == *pphead)
	{
		SListPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
	}
}

销毁

//销毁
void SListDestory(SLTNode** pphead)
{
	assert(pphead);
	SLTNode* cur = *pphead;
	while (cur)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;
}

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码

)">
< <上一篇

)">
下一篇>>